Browse Source

REST high-level client: add support for Indices Update Settings API (#28892)

Relates to #27205
olcbean 7 years ago
parent
commit
b67b5b1bbd
20 changed files with 831 additions and 26 deletions
  1. 0 1
      buildSrc/src/main/resources/checkstyle_suppressions.xml
  2. 26 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
  3. 23 1
      client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java
  4. 99 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java
  5. 28 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java
  6. 110 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java
  7. 1 1
      docs/java-rest/high-level/cluster/put_settings.asciidoc
  8. 142 0
      docs/java-rest/high-level/indices/put_settings.asciidoc
  9. 1 1
      docs/java-rest/high-level/search/search.asciidoc
  10. 2 0
      docs/java-rest/high-level/supported-apis.asciidoc
  11. 8 4
      rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_settings.json
  12. 78 2
      server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsRequest.java
  13. 14 0
      server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsResponse.java
  14. 18 1
      server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java
  15. 19 0
      server/src/main/java/org/elasticsearch/action/support/master/MasterNodeRequest.java
  16. 1 15
      server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpdateSettingsAction.java
  17. 112 0
      server/src/test/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsRequestStreamableTests.java
  18. 87 0
      server/src/test/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsRequestTests.java
  19. 46 0
      server/src/test/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsResponseTests.java
  20. 16 0
      test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

+ 0 - 1
buildSrc/src/main/resources/checkstyle_suppressions.xml

@@ -151,7 +151,6 @@
   <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]broadcast[/\\]BroadcastOperationRequestBuilder.java" checks="LineLength" />
   <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]broadcast[/\\]TransportBroadcastAction.java" checks="LineLength" />
   <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]broadcast[/\\]node[/\\]TransportBroadcastByNodeAction.java" checks="LineLength" />
-  <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]AcknowledgedRequest.java" checks="LineLength" />
   <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]AcknowledgedRequestBuilder.java" checks="LineLength" />
   <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]MasterNodeOperationRequestBuilder.java" checks="LineLength" />
   <suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]MasterNodeReadOperationRequestBuilder.java" checks="LineLength" />

+ 26 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java

@@ -45,6 +45,8 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
 import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
 import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
 import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
 
@@ -406,4 +408,28 @@ public final class IndicesClient {
         restHighLevelClient.performRequestAsyncAndParseEntity(rolloverRequest, Request::rollover, RolloverResponse::fromXContent,
                 listener, emptySet(), headers);
     }
+
+    /**
+     * Updates specific index level settings using the Update Indices Settings API
+     * <p>
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html"> Update Indices Settings
+     * API on elastic.co</a>
+     */
+    public UpdateSettingsResponse putSettings(UpdateSettingsRequest updateSettingsRequest, Header... headers) throws IOException {
+        return restHighLevelClient.performRequestAndParseEntity(updateSettingsRequest, Request::indexPutSettings,
+                UpdateSettingsResponse::fromXContent, emptySet(), headers);
+    }
+
+    /**
+     * Asynchronously updates specific index level settings using the Update Indices Settings API
+     * <p>
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html"> Update Indices Settings
+     * API on elastic.co</a>
+     */
+    public void putSettingsAsync(UpdateSettingsRequest updateSettingsRequest, ActionListener<UpdateSettingsResponse> listener,
+            Header... headers) {
+        restHighLevelClient.performRequestAsyncAndParseEntity(updateSettingsRequest, Request::indexPutSettings,
+                UpdateSettingsResponse::fromXContent, listener, emptySet(), headers);
+    }
+
 }

+ 23 - 1
client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java

@@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
 import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
 import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
 import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeType;
 import org.elasticsearch.action.bulk.BulkRequest;
@@ -598,7 +599,7 @@ public final class Request {
     }
 
     static Request indicesExist(GetIndexRequest request) {
-        //this can be called with no indices as argument by transport client, not via REST though
+        // this can be called with no indices as argument by transport client, not via REST though
         if (request.indices() == null || request.indices().length == 0) {
             throw new IllegalArgumentException("indices are mandatory");
         }
@@ -612,6 +613,20 @@ public final class Request {
         return new Request(HttpHead.METHOD_NAME, endpoint, params.getParams(), null);
     }
 
+    static Request indexPutSettings(UpdateSettingsRequest updateSettingsRequest) throws IOException {
+        Params parameters = Params.builder();
+        parameters.withTimeout(updateSettingsRequest.timeout());
+        parameters.withMasterTimeout(updateSettingsRequest.masterNodeTimeout());
+        parameters.withIndicesOptions(updateSettingsRequest.indicesOptions());
+        parameters.withFlatSettings(updateSettingsRequest.flatSettings());
+        parameters.withPreserveExisting(updateSettingsRequest.isPreserveExisting());
+
+        String[] indices = updateSettingsRequest.indices() == null ? Strings.EMPTY_ARRAY : updateSettingsRequest.indices();
+        String endpoint = endpoint(indices, "_settings");
+        HttpEntity entity = createEntity(updateSettingsRequest, REQUEST_BODY_CONTENT_TYPE);
+        return new Request(HttpPut.METHOD_NAME, endpoint, parameters.getParams(), entity);
+    }
+
     private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
         BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
         return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));
@@ -833,6 +848,13 @@ public final class Request {
             return this;
         }
 
+        Params withPreserveExisting(boolean preserveExisting) {
+            if (preserveExisting) {
+                return putParam("preserve_existing", Boolean.TRUE.toString());
+            }
+            return this;
+        }
+
         Map<String, String> getParams() {
             return Collections.unmodifiableMap(params);
         }

+ 99 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java

@@ -49,6 +49,8 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
 import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
 import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
 import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeType;
@@ -56,6 +58,8 @@ import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.WriteRequest;
 import org.elasticsearch.action.support.broadcast.BroadcastResponse;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.ByteSizeValue;
@@ -63,6 +67,7 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
 import org.elasticsearch.common.xcontent.support.XContentMapValues;
+import org.elasticsearch.index.IndexSettings;
 import org.elasticsearch.rest.RestStatus;
 
 import java.io.IOException;
@@ -72,6 +77,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF
 import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.startsWith;
 
 public class IndicesClientIT extends ESRestHighLevelClientTestCase {
 
@@ -609,4 +615,97 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
             assertEquals("test_new", rolloverResponse.getNewIndex());
         }
     }
+
+    public void testIndexPutSettings() throws IOException {
+
+        final Setting<Integer> dynamicSetting = IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING;
+        final String dynamicSettingKey = IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
+        final int dynamicSettingValue = 0;
+
+        final Setting<String> staticSetting = IndexSettings.INDEX_CHECK_ON_STARTUP;
+        final String staticSettingKey = IndexSettings.INDEX_CHECK_ON_STARTUP.getKey();
+        final String staticSettingValue = "true";
+
+        final Setting<Integer> unmodifiableSetting = IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING;
+        final String unmodifiableSettingKey = IndexMetaData.SETTING_NUMBER_OF_SHARDS;
+        final int unmodifiableSettingValue = 3;
+
+        String index = "index";
+        createIndex(index, Settings.EMPTY);
+
+        assertThat(dynamicSetting.getDefault(Settings.EMPTY), not(dynamicSettingValue));
+        UpdateSettingsRequest dynamicSettingRequest = new UpdateSettingsRequest();
+        dynamicSettingRequest.settings(Settings.builder().put(dynamicSettingKey, dynamicSettingValue).build());
+        UpdateSettingsResponse response = execute(dynamicSettingRequest, highLevelClient().indices()::putSettings,
+                highLevelClient().indices()::putSettingsAsync);
+
+        assertTrue(response.isAcknowledged());
+        Map<String, Object> indexSettingsAsMap = getIndexSettingsAsMap(index);
+        assertThat(indexSettingsAsMap.get(dynamicSettingKey), equalTo(String.valueOf(dynamicSettingValue)));
+
+        assertThat(staticSetting.getDefault(Settings.EMPTY), not(staticSettingValue));
+        UpdateSettingsRequest staticSettingRequest = new UpdateSettingsRequest();
+        staticSettingRequest.settings(Settings.builder().put(staticSettingKey, staticSettingValue).build());
+        ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(staticSettingRequest,
+                highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
+        assertThat(exception.getMessage(),
+                startsWith("Elasticsearch exception [type=illegal_argument_exception, "
+                        + "reason=Can't update non dynamic settings [[index.shard.check_on_startup]] for open indices [[index/"));
+
+        indexSettingsAsMap = getIndexSettingsAsMap(index);
+        assertNull(indexSettingsAsMap.get(staticSettingKey));
+
+        closeIndex(index);
+        response = execute(staticSettingRequest, highLevelClient().indices()::putSettings,
+                highLevelClient().indices()::putSettingsAsync);
+        assertTrue(response.isAcknowledged());
+        openIndex(index);
+        indexSettingsAsMap = getIndexSettingsAsMap(index);
+        assertThat(indexSettingsAsMap.get(staticSettingKey), equalTo(staticSettingValue));
+
+        assertThat(unmodifiableSetting.getDefault(Settings.EMPTY), not(unmodifiableSettingValue));
+        UpdateSettingsRequest unmodifiableSettingRequest = new UpdateSettingsRequest();
+        unmodifiableSettingRequest.settings(Settings.builder().put(unmodifiableSettingKey, unmodifiableSettingValue).build());
+        exception = expectThrows(ElasticsearchException.class, () -> execute(unmodifiableSettingRequest,
+                highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
+        assertThat(exception.getMessage(), startsWith(
+                "Elasticsearch exception [type=illegal_argument_exception, "
+                + "reason=Can't update non dynamic settings [[index.number_of_shards]] for open indices [[index/"));
+        closeIndex(index);
+        exception = expectThrows(ElasticsearchException.class, () -> execute(unmodifiableSettingRequest,
+                highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
+        assertThat(exception.getMessage(), startsWith(
+                "Elasticsearch exception [type=illegal_argument_exception, "
+                + "reason=final index setting [index.number_of_shards], not updateable"));
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> getIndexSettingsAsMap(String index) throws IOException {
+        Map<String, Object> indexSettings = getIndexSettings(index);
+        return (Map<String, Object>)((Map<String, Object>) indexSettings.get(index)).get("settings");
+    }
+
+    public void testIndexPutSettingNonExistent() throws IOException {
+
+        String index = "index";
+        UpdateSettingsRequest indexUpdateSettingsRequest = new UpdateSettingsRequest(index);
+        String setting = "no_idea_what_you_are_talking_about";
+        int value = 10;
+        indexUpdateSettingsRequest.settings(Settings.builder().put(setting, value).build());
+
+        ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(indexUpdateSettingsRequest,
+                highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
+        assertEquals(RestStatus.NOT_FOUND, exception.status());
+        assertThat(exception.getMessage(), equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]"));
+
+        createIndex(index, Settings.EMPTY);
+        exception = expectThrows(ElasticsearchException.class, () -> execute(indexUpdateSettingsRequest,
+                highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
+        assertThat(exception.status(), equalTo(RestStatus.BAD_REQUEST));
+        assertThat(exception.getMessage(), equalTo(
+                "Elasticsearch exception [type=illegal_argument_exception, "
+                + "reason=unknown setting [index.no_idea_what_you_are_talking_about] please check that any required plugins are installed, "
+                + "or check the breaking changes documentation for removed settings]"));
+    }
+
 }

+ 28 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java

@@ -46,6 +46,7 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
 import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
 import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
 import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeType;
 import org.elasticsearch.action.bulk.BulkRequest;
@@ -1363,6 +1364,33 @@ public class RequestTests extends ESTestCase {
         assertEquals(expectedParams, request.getParameters());
     }
 
+    public void testIndexPutSettings() throws IOException {
+        String[] indices = randomBoolean() ? null : randomIndicesNames(0, 2);
+        UpdateSettingsRequest updateSettingsRequest = new UpdateSettingsRequest(indices);
+        Map<String, String> expectedParams = new HashMap<>();
+        setRandomFlatSettings(updateSettingsRequest::flatSettings, expectedParams);
+        setRandomMasterTimeout(updateSettingsRequest, expectedParams);
+        setRandomTimeout(updateSettingsRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams);
+        setRandomIndicesOptions(updateSettingsRequest::indicesOptions, updateSettingsRequest::indicesOptions, expectedParams);
+        if (randomBoolean()) {
+            updateSettingsRequest.setPreserveExisting(randomBoolean());
+            if (updateSettingsRequest.isPreserveExisting()) {
+                expectedParams.put("preserve_existing", "true");
+            }
+        }
+
+        Request request = Request.indexPutSettings(updateSettingsRequest);
+        StringJoiner endpoint = new StringJoiner("/", "/", "");
+        if (indices != null && indices.length > 0) {
+            endpoint.add(String.join(",", indices));
+        }
+        endpoint.add("_settings");
+        assertThat(endpoint.toString(), equalTo(request.getEndpoint()));
+        assertEquals(HttpPut.METHOD_NAME, request.getMethod());
+        assertToXContentBody(updateSettingsRequest, request.getEntity());
+        assertEquals(expectedParams, request.getParameters());
+    }
+
     private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
         BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false);
         assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());

+ 110 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java

@@ -48,6 +48,8 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
 import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
 import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
 import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeType;
@@ -56,6 +58,7 @@ import org.elasticsearch.action.support.DefaultShardOperationFailedException;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.client.ESRestHighLevelClientTestCase;
 import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.ByteSizeValue;
@@ -394,6 +397,7 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
             // tag::create-index-execute-listener
             ActionListener<CreateIndexResponse> listener =
                     new ActionListener<CreateIndexResponse>() {
+
                 @Override
                 public void onResponse(CreateIndexResponse createIndexResponse) {
                     // <1>
@@ -1378,4 +1382,110 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
 
         assertTrue(latch.await(30L, TimeUnit.SECONDS));
     }
+
+    public void testIndexPutSettings() throws Exception {
+        RestHighLevelClient client = highLevelClient();
+
+        {
+            CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("index"));
+            assertTrue(createIndexResponse.isAcknowledged());
+        }
+
+        // tag::put-settings-request
+        UpdateSettingsRequest request = new UpdateSettingsRequest("index1"); // <1>
+        UpdateSettingsRequest requestMultiple =
+                new UpdateSettingsRequest("index1", "index2"); // <2>
+        UpdateSettingsRequest requestAll = new UpdateSettingsRequest(); // <3>
+        // end::put-settings-request
+
+        // tag::put-settings-create-settings
+        String settingKey = "index.number_of_replicas";
+        int settingValue = 0;
+        Settings settings =
+                Settings.builder()
+                .put(settingKey, settingValue)
+                .build(); // <1>
+        // end::put-settings-create-settings
+        // tag::put-settings-request-index-settings
+        request.settings(settings);
+        // end::put-settings-request-index-settings
+
+        {
+            // tag::put-settings-settings-builder
+            Settings.Builder settingsBuilder =
+                    Settings.builder()
+                    .put(settingKey, settingValue);
+            request.settings(settingsBuilder); // <1>
+            // end::put-settings-settings-builder
+        }
+        {
+            // tag::put-settings-settings-map
+            Map<String, Object> map = new HashMap<>();
+            map.put(settingKey, settingValue);
+            request.settings(map); // <1>
+            // end::put-settings-settings-map
+        }
+        {
+            // tag::put-settings-settings-source
+            request.settings(
+                    "{\"index.number_of_replicas\": \"2\"}"
+                    , XContentType.JSON); // <1>
+            // end::put-settings-settings-source
+        }
+
+        // tag::put-settings-request-flat-settings
+        request.flatSettings(true); // <1>
+        // end::put-settings-request-flat-settings
+        // tag::put-settings-request-preserveExisting
+        request.setPreserveExisting(false); // <1>
+        // end::put-settings-request-preserveExisting
+        // tag::put-settings-request-timeout
+        request.timeout(TimeValue.timeValueMinutes(2)); // <1>
+        request.timeout("2m"); // <2>
+        // end::put-settings-request-timeout
+        // tag::put-settings-request-masterTimeout
+        request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
+        request.masterNodeTimeout("1m"); // <2>
+        // end::put-settings-request-masterTimeout
+        // tag::put-settings-request-indicesOptions
+        request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1>
+        // end::put-settings-request-indicesOptions
+
+        // tag::put-settings-execute
+        UpdateSettingsResponse updateSettingsResponse =
+                client.indices().putSettings(request);
+        // end::put-settings-execute
+
+        // tag::put-settings-response
+        boolean acknowledged = updateSettingsResponse.isAcknowledged(); // <1>
+        // end::put-settings-response
+        assertTrue(acknowledged);
+
+        // tag::put-settings-execute-listener
+        ActionListener<UpdateSettingsResponse> listener =
+                new ActionListener<UpdateSettingsResponse>() {
+
+            @Override
+            public void onResponse(UpdateSettingsResponse updateSettingsResponse) {
+                // <1>
+            }
+
+            @Override
+            public void onFailure(Exception e) {
+                // <2>
+            }
+        };
+        // end::put-settings-execute-listener
+
+        // Replace the empty listener by a blocking listener in test
+        final CountDownLatch latch = new CountDownLatch(1);
+        listener = new LatchedActionListener<>(listener, latch);
+
+        // tag::put-settings-execute-async
+        client.indices().putSettingsAsync(request,listener); // <1>
+        // end::put-settings-execute-async
+
+        assertTrue(latch.await(30L, TimeUnit.SECONDS));
+    }
+
 }

+ 1 - 1
docs/java-rest/high-level/cluster/put_settings.asciidoc

@@ -58,7 +58,7 @@ The following arguments can optionally be provided:
 --------------------------------------------------
 include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-request-flat-settings]
 --------------------------------------------------
-<1> Wether the updated settings returned in the `ClusterUpdateSettings` should
+<1> Whether the updated settings returned in the `ClusterUpdateSettings` should
 be in a flat format
 
 ["source","java",subs="attributes,callouts,macros"]

+ 142 - 0
docs/java-rest/high-level/indices/put_settings.asciidoc

@@ -0,0 +1,142 @@
+[[java-rest-high-indices-put-settings]]
+=== Update Indices Settings API
+
+The Update Indices Settings API allows to change specific index level settings.
+
+[[java-rest-high-indices-put-settings-request]]
+==== Update Indices Settings Request
+
+An `UpdateSettingsRequest`:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request]
+--------------------------------------------------
+<1> Update settings for one index
+<2> Update settings for multiple indices
+<3> Update settings for all indices
+
+==== Indices Settings
+At least one setting to be updated must be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-create-settings]
+--------------------------------------------------
+<1> Sets the index settings to be applied
+
+==== Providing the Settings
+The settings to be applied can be provided in different ways:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-create-settings]
+--------------------------------------------------
+<1> Creates a setting as `Settings`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-settings-builder]
+--------------------------------------------------
+<1> Settings provided as `Settings.Builder`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-settings-source]
+--------------------------------------------------
+<1> Settings provided as `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-settings-map]
+--------------------------------------------------
+<1> Settings provided as a `Map`
+
+==== Optional Arguments
+The following arguments can optionally be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-flat-settings]
+--------------------------------------------------
+<1> Whether the updated settings returned in the `UpdateSettings` should
+be in a flat format
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-preserveExisting]
+--------------------------------------------------
+<1> Whether to update existing settings. If set to `true` existing settings
+on an index remain unchanged, the default is `false`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-timeout]
+--------------------------------------------------
+<1> Timeout to wait for the all the nodes to acknowledge the new setting
+as a `TimeValue`
+<2> Timeout to wait for the all the nodes to acknowledge the new setting
+as a `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-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}/IndicesClientDocumentationIT.java[put-settings-request-indicesOptions]
+--------------------------------------------------
+<1> Setting `IndicesOptions` controls how unavailable indices are resolved and
+how wildcard expressions are expanded
+
+[[java-rest-high-indices-put-settings-sync]]
+==== Synchronous Execution
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-execute]
+--------------------------------------------------
+
+[[java-rest-high-indices-put-settings-async]]
+==== Asynchronous Execution
+
+The asynchronous execution of an indices update settings requires both the
+`UpdateSettingsRequest` instance and an `ActionListener` instance to be
+passed to the asynchronous method:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-execute-async]
+--------------------------------------------------
+<1> The `UpdateSettingsRequest` 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 `UpdateSettingsResponse` looks like:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-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-indices-put-settings-response]]
+==== Update Indices Settings Response
+
+The returned `UpdateSettingsResponse` allows to retrieve information about the
+executed operation as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-response]
+--------------------------------------------------
+<1> Indicates whether all of the nodes have acknowledged the request

+ 1 - 1
docs/java-rest/high-level/search/search.asciidoc

@@ -275,7 +275,7 @@ include-tagged::{doc-tests}/SearchDocumentationIT.java[search-execute-listener]
 The `SearchResponse` that is returned by executing the search provides details
 about the search execution itself as well as access to the documents returned.
 First, there is useful information about the request execution itself, like the
-HTTP status code, execution time or wether the request terminated early or timed
+HTTP status code, execution time or whether the request terminated early or timed
 out:
 
 ["source","java",subs="attributes,callouts,macros"]

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

@@ -64,6 +64,7 @@ Index Management::
 * <<java-rest-high-clear-cache>>
 * <<java-rest-high-force-merge>>
 * <<java-rest-high-rollover-index>>
+* <<java-rest-high-indices-put-settings>>
 
 Mapping Management::
 * <<java-rest-high-put-mapping>>
@@ -87,6 +88,7 @@ include::indices/rollover.asciidoc[]
 include::indices/put_mapping.asciidoc[]
 include::indices/update_aliases.asciidoc[]
 include::indices/exists_alias.asciidoc[]
+include::indices/put_settings.asciidoc[]
 
 == Cluster APIs
 

+ 8 - 4
rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_settings.json

@@ -16,6 +16,10 @@
           "type": "time",
           "description": "Specify timeout for connection to master"
         },
+        "timeout": {
+          "type" : "time",
+          "description" : "Explicit operation timeout"
+        },
         "preserve_existing": {
           "type": "boolean",
           "description": "Whether to update existing settings. If set to `true` existing settings on an index remain unchanged, the default is `false`"
@@ -34,10 +38,10 @@
           "default": "open",
           "description": "Whether to expand wildcard expression to concrete indices that are open, closed or both."
         },
-          "flat_settings": {
-              "type": "boolean",
-              "description": "Return settings in flat format (default: false)"
-          }
+        "flat_settings": {
+            "type": "boolean",
+            "description": "Return settings in flat format (default: false)"
+        }
       }
     },
     "body": {

+ 78 - 2
server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsRequest.java

@@ -28,27 +28,34 @@ 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.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 import static org.elasticsearch.action.ValidateActions.addValidationError;
-import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
 import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
+import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
 
 /**
  * Request for an update index settings action
  */
-public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsRequest> implements IndicesRequest.Replaceable {
+public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsRequest>
+        implements IndicesRequest.Replaceable, ToXContentObject {
 
     private String[] indices;
     private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, true, true);
     private Settings settings = EMPTY_SETTINGS;
     private boolean preserveExisting = false;
+    private boolean flatSettings = false;
 
     public UpdateSettingsRequest() {
     }
@@ -68,6 +75,29 @@ public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsReq
         this.settings = settings;
     }
 
+    /**
+     * Sets the value of "flat_settings".
+     * Used only by the high-level REST client.
+     * 
+     * @param flatSettings
+     *            value of "flat_settings" flag to be set
+     * @return this request
+     */
+    public UpdateSettingsRequest flatSettings(boolean flatSettings) {
+        this.flatSettings = flatSettings;
+        return this;
+    }
+
+    /**
+     * Return settings in flat format.
+     * Used only by the high-level REST client.
+     * 
+     * @return <code>true</code> if settings need to be returned in flat format; <code>false</code> otherwise.
+     */
+    public boolean flatSettings() {
+        return flatSettings;
+    }
+
     @Override
     public ActionRequestValidationException validate() {
         ActionRequestValidationException validationException = null;
@@ -178,4 +208,50 @@ public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsReq
         writeSettingsToStream(settings, out);
         out.writeBoolean(preserveExisting);
     }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        settings.toXContent(builder, params);
+        builder.endObject();
+        return builder;
+    }
+
+    public UpdateSettingsRequest fromXContent(XContentParser parser) throws IOException {
+        Map<String, Object> settings = new HashMap<>();
+        Map<String, Object> bodySettings = parser.map();
+        Object innerBodySettings = bodySettings.get("settings");
+        // clean up in case the body is wrapped with "settings" : { ... }
+        if (innerBodySettings instanceof Map) {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> innerBodySettingsMap = (Map<String, Object>) innerBodySettings;
+            settings.putAll(innerBodySettingsMap);
+        } else {
+            settings.putAll(bodySettings);
+        }
+        return this.settings(settings);
+    }
+
+    @Override
+    public String toString() {
+        return "indices : " + Arrays.toString(indices) + "," + Strings.toString(this);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (super.equals(o)) {
+            UpdateSettingsRequest that = (UpdateSettingsRequest) o;
+            return Objects.equals(settings, that.settings)
+                    && Objects.equals(indicesOptions, that.indicesOptions)
+                    && Objects.equals(preserveExisting, that.preserveExisting)
+                    && Arrays.equals(indices, that.indices);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), settings, indicesOptions, preserveExisting, Arrays.hashCode(indices));
+    }
+
 }

+ 14 - 0
server/src/main/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsResponse.java

@@ -22,6 +22,8 @@ package org.elasticsearch.action.admin.indices.settings.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 UpdateSettingsResponse extends AcknowledgedResponse {
 
+    private static final ConstructingObjectParser<UpdateSettingsResponse, Void> PARSER = new ConstructingObjectParser<>(
+            "update_index_settings", true, args -> new UpdateSettingsResponse((boolean) args[0]));
+
+    static {
+        declareAcknowledgedField(PARSER);
+    }
+
     UpdateSettingsResponse() {
     }
 
@@ -48,4 +57,9 @@ public class UpdateSettingsResponse extends AcknowledgedResponse {
         super.writeTo(out);
         writeAcknowledged(out);
     }
+
+    public static UpdateSettingsResponse fromXContent(XContentParser parser) {
+        return PARSER.apply(parser, null);
+    }
+
 }

+ 18 - 1
server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java

@@ -24,6 +24,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.unit.TimeValue;
 
 import java.io.IOException;
+import java.util.Objects;
 
 import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
 
@@ -31,7 +32,8 @@ import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
  * Abstract class that allows to mark action requests that support acknowledgements.
  * Facilitates consistency across different api.
  */
-public abstract class AcknowledgedRequest<Request extends MasterNodeRequest<Request>> extends MasterNodeRequest<Request> implements AckedRequest {
+public abstract class AcknowledgedRequest<Request extends MasterNodeRequest<Request>> extends MasterNodeRequest<Request>
+        implements AckedRequest {
 
     public static final TimeValue DEFAULT_ACK_TIMEOUT = timeValueSeconds(30);
 
@@ -86,4 +88,19 @@ public abstract class AcknowledgedRequest<Request extends MasterNodeRequest<Requ
         super.writeTo(out);
         timeout.writeTo(out);
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (super.equals(o)) {
+            AcknowledgedRequest<Request> that = (AcknowledgedRequest<Request>) o;
+            return Objects.equals(timeout, that.timeout);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), timeout);
+    }
+
 }

+ 19 - 0
server/src/main/java/org/elasticsearch/action/support/master/MasterNodeRequest.java

@@ -25,6 +25,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.unit.TimeValue;
 
 import java.io.IOException;
+import java.util.Objects;
 
 /**
  * A based request for master based operation.
@@ -76,4 +77,22 @@ public abstract class MasterNodeRequest<Request extends MasterNodeRequest<Reques
         super.readFrom(in);
         masterNodeTimeout = new TimeValue(in);
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MasterNodeRequest<Request> that = (MasterNodeRequest<Request>) o;
+        return Objects.equals(masterNodeTimeout, that.masterNodeTimeout);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(masterNodeTimeout);
+    }
+
 }

+ 1 - 15
server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpdateSettingsAction.java

@@ -57,21 +57,7 @@ public class RestUpdateSettingsAction extends BaseRestHandler {
         updateSettingsRequest.setPreserveExisting(request.paramAsBoolean("preserve_existing", updateSettingsRequest.isPreserveExisting()));
         updateSettingsRequest.masterNodeTimeout(request.paramAsTime("master_timeout", updateSettingsRequest.masterNodeTimeout()));
         updateSettingsRequest.indicesOptions(IndicesOptions.fromRequest(request, updateSettingsRequest.indicesOptions()));
-
-        Map<String, Object> settings = new HashMap<>();
-        try (XContentParser parser = request.contentParser()) {
-            Map<String, Object> bodySettings = parser.map();
-            Object innerBodySettings = bodySettings.get("settings");
-            // clean up in case the body is wrapped with "settings" : { ... }
-            if (innerBodySettings instanceof Map) {
-                @SuppressWarnings("unchecked")
-                Map<String, Object> innerBodySettingsMap = (Map<String, Object>) innerBodySettings;
-                settings.putAll(innerBodySettingsMap);
-            } else {
-                settings.putAll(bodySettings);
-            }
-        }
-        updateSettingsRequest.settings(settings);
+        updateSettingsRequest.fromXContent(request.contentParser());
 
         return channel -> client.admin().indices().updateSettings(updateSettingsRequest, new RestToXContentListener<>(channel));
     }

+ 112 - 0
server/src/test/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsRequestStreamableTests.java

@@ -0,0 +1,112 @@
+/*
+ * 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.indices.settings.put;
+
+import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.settings.Settings.Builder;
+import org.elasticsearch.common.util.CollectionUtils;
+import org.elasticsearch.test.AbstractStreamableTestCase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.StringJoiner;
+
+public class UpdateSettingsRequestStreamableTests extends AbstractStreamableTestCase<UpdateSettingsRequest> {
+
+    @Override
+    protected UpdateSettingsRequest mutateInstance(UpdateSettingsRequest request) {
+        if (randomBoolean()) {
+            return new UpdateSettingsRequest(mutateSettings(request.settings()), request.indices());
+        }
+        return new UpdateSettingsRequest(request.settings(), mutateIndices(request.indices()));
+    }
+
+    @Override
+    protected UpdateSettingsRequest createTestInstance() {
+        return createTestItem();
+    }
+
+    @Override
+    protected UpdateSettingsRequest createBlankInstance() {
+        return new UpdateSettingsRequest();
+    }
+
+    public static UpdateSettingsRequest createTestItem() {
+        UpdateSettingsRequest request = randomBoolean()
+                ? new UpdateSettingsRequest(randomSettings(0, 2))
+                : new UpdateSettingsRequest(randomSettings(0, 2), randomIndicesNames(0, 2));
+        request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()));
+        request.setPreserveExisting(randomBoolean());
+        request.flatSettings(randomBoolean());
+        return request;
+    }
+
+    private static Settings mutateSettings(Settings settings) {
+        if (settings.isEmpty()) {
+            return randomSettings(1, 5);
+        }
+        Set<String> allKeys = settings.keySet();
+        List<String> keysToBeModified = randomSubsetOf(randomIntBetween(1, allKeys.size()), allKeys);
+        Builder builder = Settings.builder();
+        for (String key : allKeys) {
+            String value = settings.get(key);
+            if (keysToBeModified.contains(key)) {
+                value += randomAlphaOfLengthBetween(2, 5);
+            }
+            builder.put(key, value);
+        }
+        return builder.build();
+    }
+
+    private static String[] mutateIndices(String[] indices) {
+        if (CollectionUtils.isEmpty(indices)) {
+            return randomIndicesNames(1, 5);
+        }
+        String[] mutated = Arrays.copyOf(indices, indices.length);
+        Arrays.asList(mutated).replaceAll(i -> i += randomAlphaOfLengthBetween(2, 5));
+        return mutated;
+    }
+
+    private static Settings randomSettings(int min, int max) {
+        int num = randomIntBetween(min, max);
+        Builder builder = Settings.builder();
+        for (int i = 0; i < num; i++) {
+            int keyDepth = randomIntBetween(1, 5);
+            StringJoiner keyJoiner = new StringJoiner(".", "", "");
+            for (int d = 0; d < keyDepth; d++) {
+                keyJoiner.add(randomAlphaOfLengthBetween(3, 5));
+            }
+            builder.put(keyJoiner.toString(), randomAlphaOfLengthBetween(2, 5));
+        }
+        return builder.build();
+    }
+
+    private static String[] randomIndicesNames(int minIndicesNum, int maxIndicesNum) {
+        int numIndices = randomIntBetween(minIndicesNum, maxIndicesNum);
+        String[] indices = new String[numIndices];
+        for (int i = 0; i < numIndices; i++) {
+            indices[i] = "index-" + randomAlphaOfLengthBetween(2, 5).toLowerCase(Locale.ROOT);
+        }
+        return indices;
+    }
+}

+ 87 - 0
server/src/test/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsRequestTests.java

@@ -0,0 +1,87 @@
+/*
+ * 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.indices.settings.put;
+
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractXContentTestCase;
+
+import java.io.IOException;
+import java.util.function.Predicate;
+
+public class UpdateSettingsRequestTests extends AbstractXContentTestCase<UpdateSettingsRequest> {
+
+    private final boolean enclosedSettings = randomBoolean();
+
+    @Override
+    protected UpdateSettingsRequest createTestInstance() {
+        UpdateSettingsRequest testRequest = UpdateSettingsRequestStreamableTests.createTestItem();
+        if (enclosedSettings) {
+            UpdateSettingsRequest requestWithEnclosingSettings = new UpdateSettingsRequest(testRequest.settings()) {
+                public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+                    builder.startObject();
+                    builder.startObject("settings");
+                    this.settings().toXContent(builder, params);
+                    builder.endObject();
+                    builder.endObject();
+                    return builder;
+                }
+            };
+            return requestWithEnclosingSettings;
+        }
+        return testRequest;
+    }
+
+    @Override
+    protected UpdateSettingsRequest doParseInstance(XContentParser parser) throws IOException {
+        return new UpdateSettingsRequest().fromXContent(parser);
+    }
+
+    @Override
+    protected boolean supportsUnknownFields() {
+        // if the settings are enclose as a "settings" object
+        // then all other top-level elements will be ignored during the parsing
+        return enclosedSettings;
+    }
+
+    @Override
+    protected Predicate<String> getRandomFieldsExcludeFilter() {
+        if (enclosedSettings) {
+            return field -> field.startsWith("settings");
+        }
+        return field -> true;
+    }
+
+    @Override
+    protected void assertEqualInstances(UpdateSettingsRequest expectedInstance, UpdateSettingsRequest newInstance) {
+        // here only the settings should be tested, as this test covers explicitly only the XContent parsing
+        // the rest of the request fields are tested by the StreamableTests
+        super.assertEqualInstances(new UpdateSettingsRequest(expectedInstance.settings()),
+                new UpdateSettingsRequest(newInstance.settings()));
+    }
+
+    @Override
+    protected boolean assertToXContentEquivalence() {
+        // if enclosedSettings are used, disable the XContentEquivalence check as the
+        // parsed.toXContent is not equivalent to the test instance
+        return !enclosedSettings;
+    }
+
+}

+ 46 - 0
server/src/test/java/org/elasticsearch/action/admin/indices/settings/put/UpdateSettingsResponseTests.java

@@ -0,0 +1,46 @@
+/*
+ * 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.indices.settings.put;
+
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractStreamableXContentTestCase;
+
+public class UpdateSettingsResponseTests extends AbstractStreamableXContentTestCase<UpdateSettingsResponse> {
+
+    @Override
+    protected UpdateSettingsResponse doParseInstance(XContentParser parser) {
+        return UpdateSettingsResponse.fromXContent(parser);
+    }
+
+    @Override
+    protected UpdateSettingsResponse createTestInstance() {
+        return new UpdateSettingsResponse(randomBoolean());
+    }
+
+    @Override
+    protected UpdateSettingsResponse createBlankInstance() {
+        return new UpdateSettingsResponse();
+    }
+
+    @Override
+    protected UpdateSettingsResponse mutateInstance(UpdateSettingsResponse response) {
+        return new UpdateSettingsResponse(response.isAcknowledged() == false);
+    }
+}

+ 16 - 0
test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

@@ -32,6 +32,7 @@ import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
 import org.apache.http.ssl.SSLContexts;
 import org.elasticsearch.core.internal.io.IOUtils;
 import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksAction;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
 import org.elasticsearch.client.RestClient;
@@ -491,6 +492,16 @@ public abstract class ESRestTestCase extends ESTestCase {
             new StringEntity(Strings.toString(settings), ContentType.APPLICATION_JSON)));
     }
 
+    protected static Map<String, Object> getIndexSettings(String index) throws IOException {
+        Map<String, String> params = new HashMap<>();
+        params.put("flat_settings", "true");
+        Response response = client().performRequest(HttpGet.METHOD_NAME, index + "/_settings", params);
+        assertOK(response);
+        try (InputStream is = response.getEntity().getContent()) {
+            return XContentHelper.convertToMap(XContentType.JSON.xContent(), is, true);
+        }
+    }
+
     protected static boolean indexExists(String index) throws IOException {
         Response response = client().performRequest(HttpHead.METHOD_NAME, index);
         return RestStatus.OK.getStatus() == response.getStatusLine().getStatusCode();
@@ -501,6 +512,11 @@ public abstract class ESRestTestCase extends ESTestCase {
         assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
     }
 
+    protected static void openIndex(String index) throws IOException {
+        Response response = client().performRequest(HttpPost.METHOD_NAME, index + "/_open");
+        assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
+    }
+
     protected static boolean aliasExists(String alias) throws IOException {
         Response response = client().performRequest(HttpHead.METHOD_NAME, "/_alias/" + alias);
         return RestStatus.OK.getStatus() == response.getStatusLine().getStatusCode();