Browse Source

[hlrc] add index templates exist API (#36132)

This commit adds support for the index templates exist API, creating
new client-side request types for that API and the get index
templates API. Also adds links in hlrc docs to pages for supported
index template APIs
Andy Bristol 6 years ago
parent
commit
10938e5135

+ 31 - 1
client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java

@@ -54,7 +54,6 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest
 import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
 import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
-import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
 import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
 import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
 import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
@@ -62,6 +61,8 @@ import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRespon
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.client.core.ShardsAcknowledgedResponse;
 import org.elasticsearch.client.indices.FreezeIndexRequest;
+import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
+import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
 import org.elasticsearch.client.indices.UnfreezeIndexRequest;
 import org.elasticsearch.rest.RestStatus;
 
@@ -813,6 +814,35 @@ public final class IndicesClient {
             options, GetIndexTemplatesResponse::fromXContent, listener, emptySet());
     }
 
+    /**
+     * Uses the Index Templates API to determine if index templates exist
+     *
+     * @param indexTemplatesRequest the request
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return true if any index templates in the request exist, false otherwise
+     * @throws IOException in case there is a problem sending the request or parsing back the response
+     */
+    public boolean existsTemplate(IndexTemplatesExistRequest indexTemplatesRequest, RequestOptions options) throws IOException {
+        return restHighLevelClient.performRequest(indexTemplatesRequest, IndicesRequestConverters::templatesExist, options,
+            RestHighLevelClient::convertExistsResponse, emptySet());
+    }
+
+    /**
+     * Uses the Index Templates API to determine if index templates exist
+     *
+     * @param indexTemplatesExistRequest the request
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @param listener the listener to be notified upon request completion. The listener will be called with the value {@code true}
+     *                 if any index templates in the request exist, false otherwise
+     */
+    public void existsTemplateAsync(IndexTemplatesExistRequest indexTemplatesExistRequest,
+                                    RequestOptions options,
+                                    ActionListener<Boolean> listener) {
+
+        restHighLevelClient.performRequestAsync(indexTemplatesExistRequest, IndicesRequestConverters::templatesExist, options,
+            RestHighLevelClient::convertExistsResponse, listener, emptySet());
+    }
+
     /**
      * Calls the analyze API
      *

+ 23 - 8
client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java

@@ -46,10 +46,11 @@ 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.admin.indices.template.delete.DeleteIndexTemplateRequest;
-import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
 import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
 import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
 import org.elasticsearch.client.indices.FreezeIndexRequest;
+import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
+import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
 import org.elasticsearch.client.indices.UnfreezeIndexRequest;
 import org.elasticsearch.common.Strings;
 
@@ -385,13 +386,27 @@ final class IndicesRequestConverters {
         return request;
     }
 
-    static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) throws IOException {
-        String[] names = getIndexTemplatesRequest.names();
-        String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_template").addCommaSeparatedPathParts(names).build();
-        Request request = new Request(HttpGet.METHOD_NAME, endpoint);
-        RequestConverters.Params params = new RequestConverters.Params(request);
-        params.withLocal(getIndexTemplatesRequest.local());
-        params.withMasterTimeout(getIndexTemplatesRequest.masterNodeTimeout());
+    static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) {
+        final String endpoint = new RequestConverters.EndpointBuilder()
+            .addPathPartAsIs("_template")
+            .addCommaSeparatedPathParts(getIndexTemplatesRequest.names())
+            .build();
+        final Request request = new Request(HttpGet.METHOD_NAME, endpoint);
+        final RequestConverters.Params params = new RequestConverters.Params(request);
+        params.withLocal(getIndexTemplatesRequest.isLocal());
+        params.withMasterTimeout(getIndexTemplatesRequest.getMasterNodeTimeout());
+        return request;
+    }
+
+    static Request templatesExist(IndexTemplatesExistRequest indexTemplatesExistRequest) {
+        final String endpoint = new RequestConverters.EndpointBuilder()
+            .addPathPartAsIs("_template")
+            .addCommaSeparatedPathParts(indexTemplatesExistRequest.names())
+            .build();
+        final Request request = new Request(HttpHead.METHOD_NAME, endpoint);
+        final RequestConverters.Params params = new RequestConverters.Params(request);
+        params.withLocal(indexTemplatesExistRequest.isLocal());
+        params.withMasterTimeout(indexTemplatesExistRequest.getMasterNodeTimeout());
         return request;
     }
 

+ 99 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplatesRequest.java

@@ -0,0 +1,99 @@
+/*
+ * 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.client.indices;
+
+import org.elasticsearch.client.TimedRequest;
+import org.elasticsearch.client.Validatable;
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.unit.TimeValue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+import static java.util.Collections.unmodifiableList;
+
+/**
+ * A request to read the content of index templates
+ */
+public class GetIndexTemplatesRequest implements Validatable {
+
+    private final List<String> names;
+
+    private TimeValue masterNodeTimeout = TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT;
+    private boolean local = false;
+
+    /**
+     * Create a request to read the content of one or more index templates. If no template names are provided, all templates will be read
+     *
+     * @param names the names of templates to read
+     */
+    public GetIndexTemplatesRequest(String... names) {
+        this(Arrays.asList(names));
+    }
+
+    /**
+     * Create a request to read the content of one or more index templates. If no template names are provided, all templates will be read
+     *
+     * @param names the names of templates to read
+     */
+    public GetIndexTemplatesRequest(List<String> names) {
+        Objects.requireNonNull(names);
+        if (names.stream().anyMatch(name -> name == null || Strings.hasText(name) == false)) {
+            throw new IllegalArgumentException("all index template names must be non null and non empty");
+        }
+        this.names = unmodifiableList(names);
+    }
+
+    /**
+     * @return the names of index templates this request is requesting
+     */
+    public List<String> names() {
+        return names;
+    }
+
+    /**
+     * @return the timeout for waiting for the master node to respond
+     */
+    public TimeValue getMasterNodeTimeout() {
+        return masterNodeTimeout;
+    }
+
+    public void setMasterNodeTimeout(@Nullable TimeValue masterNodeTimeout) {
+        this.masterNodeTimeout = masterNodeTimeout;
+    }
+
+    public void setMasterNodeTimeout(String masterNodeTimeout) {
+        final TimeValue timeValue = TimeValue.parseTimeValue(masterNodeTimeout, getClass().getSimpleName() + ".masterNodeTimeout");
+        setMasterNodeTimeout(timeValue);
+    }
+
+    /**
+     * @return true if this request is to read from the local cluster state, rather than the master node - false otherwise
+     */
+    public boolean isLocal() {
+        return local;
+    }
+
+    public void setLocal(boolean local) {
+        this.local = local;
+    }
+}

+ 50 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/indices/IndexTemplatesExistRequest.java

@@ -0,0 +1,50 @@
+/*
+ * 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.client.indices;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A request to check for the existence of index templates
+ */
+public class IndexTemplatesExistRequest extends GetIndexTemplatesRequest {
+
+    /**
+     * Create a request to check for the existence of index templates. At least one template index name must be provided
+     *
+     * @param names the names of templates to check for the existence of
+     */
+    public IndexTemplatesExistRequest(String... names) {
+        this(Arrays.asList(names));
+    }
+
+    /**
+     * Create a request to check for the existence of index templates. At least one template index name must be provided
+     *
+     * @param names the names of templates to check for the existence of
+     */
+    public IndexTemplatesExistRequest(List<String> names) {
+        super(names);
+        if (names().isEmpty()) {
+            throw new IllegalArgumentException("must provide at least one index template name");
+        }
+    }
+}

+ 52 - 11
client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java

@@ -61,7 +61,6 @@ import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeType;
 import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
-import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
 import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
 import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
 import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
@@ -73,6 +72,8 @@ import org.elasticsearch.action.support.broadcast.BroadcastResponse;
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.client.core.ShardsAcknowledgedResponse;
 import org.elasticsearch.client.indices.FreezeIndexRequest;
+import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
+import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
 import org.elasticsearch.client.indices.UnfreezeIndexRequest;
 import org.elasticsearch.cluster.metadata.AliasMetaData;
 import org.elasticsearch.cluster.metadata.IndexMetaData;
@@ -96,7 +97,9 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
 import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS;
@@ -107,7 +110,9 @@ import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.hasItems;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.notNullValue;
@@ -1327,7 +1332,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
         assertThat(execute(putTemplate2, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(),
             equalTo(true));
 
-        GetIndexTemplatesResponse getTemplate1 = execute(new GetIndexTemplatesRequest().names("template-1"),
+        GetIndexTemplatesResponse getTemplate1 = execute(new GetIndexTemplatesRequest("template-1"),
             client.indices()::getTemplate, client.indices()::getTemplateAsync);
         assertThat(getTemplate1.getIndexTemplates(), hasSize(1));
         IndexTemplateMetaData template1 = getTemplate1.getIndexTemplates().get(0);
@@ -1335,7 +1340,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
         assertThat(template1.patterns(), contains("pattern-1", "name-1"));
         assertTrue(template1.aliases().containsKey("alias-1"));
 
-        GetIndexTemplatesResponse getTemplate2 = execute(new GetIndexTemplatesRequest().names("template-2"),
+        GetIndexTemplatesResponse getTemplate2 = execute(new GetIndexTemplatesRequest("template-2"),
             client.indices()::getTemplate, client.indices()::getTemplateAsync);
         assertThat(getTemplate2.getIndexTemplates(), hasSize(1));
         IndexTemplateMetaData template2 = getTemplate2.getIndexTemplates().get(0);
@@ -1345,20 +1350,24 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
         assertThat(template2.settings().get("index.number_of_shards"), equalTo("2"));
         assertThat(template2.settings().get("index.number_of_replicas"), equalTo("0"));
 
-        GetIndexTemplatesRequest getBothRequest = new GetIndexTemplatesRequest();
-        if (randomBoolean()) {
-            getBothRequest.names("*-1", "template-2");
-        } else {
-            getBothRequest.names("template-*");
-        }
+        List<String> names = randomBoolean()
+            ? Arrays.asList("*-1", "template-2")
+            : Arrays.asList("template-*");
+        GetIndexTemplatesRequest getBothRequest = new GetIndexTemplatesRequest(names);
         GetIndexTemplatesResponse getBoth = execute(getBothRequest, client.indices()::getTemplate, client.indices()::getTemplateAsync);
         assertThat(getBoth.getIndexTemplates(), hasSize(2));
         assertThat(getBoth.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).toArray(),
             arrayContainingInAnyOrder("template-1", "template-2"));
 
+        GetIndexTemplatesRequest getAllRequest = new GetIndexTemplatesRequest();
+        GetIndexTemplatesResponse getAll = execute(getAllRequest, client.indices()::getTemplate, client.indices()::getTemplateAsync);
+        assertThat(getAll.getIndexTemplates().size(), greaterThanOrEqualTo(2));
+        assertThat(getAll.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).collect(Collectors.toList()),
+            hasItems("template-1", "template-2"));
+
         assertTrue(execute(new DeleteIndexTemplateRequest("template-1"),
             client.indices()::deleteTemplate, client.indices()::deleteTemplateAsync).isAcknowledged());
-        assertThat(expectThrows(ElasticsearchException.class, () -> execute(new GetIndexTemplatesRequest().names("template-1"),
+        assertThat(expectThrows(ElasticsearchException.class, () -> execute(new GetIndexTemplatesRequest("template-1"),
             client.indices()::getTemplate, client.indices()::getTemplateAsync)).status(), equalTo(RestStatus.NOT_FOUND));
         assertThat(expectThrows(ElasticsearchException.class, () -> execute(new DeleteIndexTemplateRequest("template-1"),
             client.indices()::deleteTemplate, client.indices()::deleteTemplateAsync)).status(), equalTo(RestStatus.NOT_FOUND));
@@ -1370,10 +1379,42 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
 
         assertTrue(execute(new DeleteIndexTemplateRequest("template-*"),
             client.indices()::deleteTemplate, client.indices()::deleteTemplateAsync).isAcknowledged());
-        assertThat(expectThrows(ElasticsearchException.class, () -> execute(new GetIndexTemplatesRequest().names("template-*"),
+        assertThat(expectThrows(ElasticsearchException.class, () -> execute(new GetIndexTemplatesRequest("template-*"),
             client.indices()::getTemplate, client.indices()::getTemplateAsync)).status(), equalTo(RestStatus.NOT_FOUND));
     }
 
+    public void testIndexTemplatesExist() throws Exception {
+        final RestHighLevelClient client = highLevelClient();
+
+        {
+            for (String suffix : Arrays.asList("1", "2")) {
+
+                final PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest()
+                    .name("template-" + suffix)
+                    .patterns(Arrays.asList("pattern-" + suffix, "name-" + suffix))
+                    .alias(new Alias("alias-" + suffix));
+                assertTrue(execute(putRequest, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged());
+
+                final IndexTemplatesExistRequest existsRequest = new IndexTemplatesExistRequest("template-" + suffix);
+                assertTrue(execute(existsRequest, client.indices()::existsTemplate, client.indices()::existsTemplateAsync));
+            }
+        }
+
+        {
+            final List<String> templateNames = randomBoolean()
+                ? Arrays.asList("*-1", "template-2")
+                : Arrays.asList("template-*");
+
+            final IndexTemplatesExistRequest bothRequest = new IndexTemplatesExistRequest(templateNames);
+            assertTrue(execute(bothRequest, client.indices()::existsTemplate, client.indices()::existsTemplateAsync));
+        }
+
+        {
+            final IndexTemplatesExistRequest neitherRequest = new IndexTemplatesExistRequest("neither-*");
+            assertFalse(execute(neitherRequest, client.indices()::existsTemplate, client.indices()::existsTemplateAsync));
+        }
+    }
+
     public void testAnalyze() throws Exception {
 
         RestHighLevelClient client = highLevelClient();

+ 38 - 4
client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesRequestConvertersTests.java

@@ -49,10 +49,11 @@ 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.admin.indices.template.delete.DeleteIndexTemplateRequest;
-import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
 import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
 import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
 import org.elasticsearch.action.support.master.AcknowledgedRequest;
+import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
+import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
 import org.elasticsearch.common.CheckedFunction;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
@@ -71,6 +72,8 @@ import java.util.Map;
 import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
 import static org.elasticsearch.index.RandomCreateIndexGenerator.randomAliases;
 import static org.elasticsearch.index.RandomCreateIndexGenerator.randomCreateIndexRequest;
 import static org.elasticsearch.index.RandomCreateIndexGenerator.randomIndexSettings;
@@ -881,15 +884,46 @@ public class IndicesRequestConvertersTests extends ESTestCase {
         encodes.put("template-*", "template-*");
         encodes.put("foo^bar", "foo%5Ebar");
         List<String> names = ESTestCase.randomSubsetOf(1, encodes.keySet());
-        GetIndexTemplatesRequest getTemplatesRequest = new GetIndexTemplatesRequest().names(names.toArray(new String[0]));
+        GetIndexTemplatesRequest getTemplatesRequest = new GetIndexTemplatesRequest(names);
         Map<String, String> expectedParams = new HashMap<>();
-        RequestConvertersTests.setRandomMasterTimeout(getTemplatesRequest, expectedParams);
-        RequestConvertersTests.setRandomLocal(getTemplatesRequest, expectedParams);
+        RequestConvertersTests.setRandomMasterTimeout(getTemplatesRequest::setMasterNodeTimeout, expectedParams);
+        RequestConvertersTests.setRandomLocal(getTemplatesRequest::setLocal, expectedParams);
         Request request = IndicesRequestConverters.getTemplates(getTemplatesRequest);
         Assert.assertThat(request.getEndpoint(),
             equalTo("/_template/" + names.stream().map(encodes::get).collect(Collectors.joining(","))));
         Assert.assertThat(request.getParameters(), equalTo(expectedParams));
         Assert.assertThat(request.getEntity(), nullValue());
+
+        expectThrows(NullPointerException.class, () -> new GetIndexTemplatesRequest((String[]) null));
+        expectThrows(NullPointerException.class, () -> new GetIndexTemplatesRequest((List<String>) null));
+        expectThrows(IllegalArgumentException.class, () -> new GetIndexTemplatesRequest(singletonList(randomBoolean() ? "" : null)));
+        expectThrows(IllegalArgumentException.class, () -> new GetIndexTemplatesRequest(new String[] { (randomBoolean() ? "" : null) }));
+    }
+
+    public void testTemplatesExistRequest() {
+        final int numberOfNames = ESTestCase.usually()
+            ? 1
+            : ESTestCase.randomIntBetween(2, 20);
+        final List<String> names = Arrays.asList(ESTestCase.randomArray(numberOfNames, numberOfNames, String[]::new,
+            () -> ESTestCase.randomAlphaOfLengthBetween(1, 100)));
+        final Map<String, String> expectedParams = new HashMap<>();
+        final IndexTemplatesExistRequest indexTemplatesExistRequest = new IndexTemplatesExistRequest(names);
+        RequestConvertersTests.setRandomMasterTimeout(indexTemplatesExistRequest::setMasterNodeTimeout, expectedParams);
+        RequestConvertersTests.setRandomLocal(indexTemplatesExistRequest::setLocal, expectedParams);
+        assertThat(indexTemplatesExistRequest.names(), equalTo(names));
+
+        final Request request = IndicesRequestConverters.templatesExist(indexTemplatesExistRequest);
+        assertThat(request.getMethod(), equalTo(HttpHead.METHOD_NAME));
+        assertThat(request.getEndpoint(), equalTo("/_template/" + String.join(",", names)));
+        assertThat(request.getParameters(), equalTo(expectedParams));
+        assertThat(request.getEntity(), nullValue());
+
+        expectThrows(NullPointerException.class, () -> new IndexTemplatesExistRequest((String[]) null));
+        expectThrows(NullPointerException.class, () -> new IndexTemplatesExistRequest((List<String>) null));
+        expectThrows(IllegalArgumentException.class, () -> new IndexTemplatesExistRequest(new String[] { (randomBoolean() ? "" : null) }));
+        expectThrows(IllegalArgumentException.class, () -> new IndexTemplatesExistRequest(singletonList(randomBoolean() ? "" : null)));
+        expectThrows(IllegalArgumentException.class, () -> new IndexTemplatesExistRequest(new String[] {}));
+        expectThrows(IllegalArgumentException.class, () -> new IndexTemplatesExistRequest(emptyList()));
     }
 
     public void testDeleteTemplateRequest() {

+ 0 - 1
client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java

@@ -669,7 +669,6 @@ public class RestHighLevelClientTests extends ESTestCase {
             "create",
             "get_source",
             "indices.delete_alias",
-            "indices.exists_template",
             "indices.exists_type",
             "indices.get_upgrade",
             "indices.put_alias",

+ 59 - 5
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java

@@ -60,7 +60,6 @@ import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
 import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
 import org.elasticsearch.action.admin.indices.shrink.ResizeType;
 import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
-import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
 import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
 import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
 import org.elasticsearch.action.admin.indices.validate.query.QueryExplanation;
@@ -76,6 +75,8 @@ import org.elasticsearch.client.GetAliasesResponse;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.RestHighLevelClient;
 import org.elasticsearch.client.SyncedFlushResponse;
+import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
+import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
 import org.elasticsearch.client.indices.UnfreezeIndexRequest;
 import org.elasticsearch.client.core.ShardsAcknowledgedResponse;
 import org.elasticsearch.cluster.metadata.AliasMetaData;
@@ -2283,13 +2284,13 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
 
         // tag::get-templates-request
         GetIndexTemplatesRequest request = new GetIndexTemplatesRequest("my-template"); // <1>
-        request.names("template-1", "template-2"); // <2>
-        request.names("my-*"); // <3>
+        request = new GetIndexTemplatesRequest("template-1", "template-2"); // <2>
+        request = new GetIndexTemplatesRequest("my-*"); // <3>
         // end::get-templates-request
 
         // tag::get-templates-request-masterTimeout
-        request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
-        request.masterNodeTimeout("1m"); // <2>
+        request.setMasterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
+        request.setMasterNodeTimeout("1m"); // <2>
         // end::get-templates-request-masterTimeout
 
         // tag::get-templates-execute
@@ -2329,6 +2330,59 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
         assertTrue(latch.await(30L, TimeUnit.SECONDS));
     }
 
+    public void testTemplatesExist() throws Exception {
+        final RestHighLevelClient client = highLevelClient();
+        {
+            final PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest("my-template");
+            putRequest.patterns(Collections.singletonList("foo"));
+            assertTrue(client.indices().putTemplate(putRequest, RequestOptions.DEFAULT).isAcknowledged());
+        }
+
+        {
+            // tag::templates-exist-request
+            IndexTemplatesExistRequest request;
+            request = new IndexTemplatesExistRequest("my-template"); // <1>
+            request = new IndexTemplatesExistRequest("template-1", "template-2"); // <2>
+            request = new IndexTemplatesExistRequest("my-*"); // <3>
+            // end::templates-exist-request
+
+            // tag::templates-exist-request-optionals
+            request.setLocal(true); // <1>
+            request.setMasterNodeTimeout(TimeValue.timeValueMinutes(1)); // <2>
+            request.setMasterNodeTimeout("1m"); // <3>
+            // end::templates-exist-request-optionals
+
+            // tag::templates-exist-execute
+            boolean exists = client.indices().existsTemplate(request, RequestOptions.DEFAULT);
+            // end::templates-exist-execute
+            assertTrue(exists);
+
+            // tag::templates-exist-execute-listener
+            ActionListener<Boolean> listener = new ActionListener<Boolean>() {
+                @Override
+                public void onResponse(Boolean aBoolean) {
+                    // <1>
+                }
+
+                @Override
+                public void onFailure(Exception e) {
+                    // <2>
+                }
+            };
+            // end::templates-exist-execute-listener
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            listener = new LatchedActionListener<>(listener, latch);
+
+            // tag::templates-exist-execute-async
+            client.indices().existsTemplateAsync(request, RequestOptions.DEFAULT, listener); // <1>
+            // end::templates-exist-execute-async
+
+            assertTrue(latch.await(30L, TimeUnit.SECONDS));
+        }
+
+    }
+
     @SuppressWarnings("unused")
     public void testValidateQuery() throws IOException, InterruptedException {
         RestHighLevelClient client = highLevelClient();

+ 41 - 0
docs/java-rest/high-level/indices/templates_exist.asciidoc

@@ -0,0 +1,41 @@
+--
+:api: templates-exist
+:request: IndexTemplatesExistRequest
+:response: Boolean
+--
+
+[id="{upid}-{api}"]
+=== Templates Exist API
+
+[id="{upid}-{api}-request"]
+==== Templates Exist Request
+
+The Templates Exist API uses +{request}+ as its request object. One or more
+index template names can be provided at construction.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request]
+--------------------------------------------------
+<1> A single index template name
+<2> Multiple index template names
+<3> An index template name using wildcard
+
+==== Optional arguments
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request-optionals]
+--------------------------------------------------
+<1> If `true`, reads templates from the node's local cluster state. Otherwise
+reads from the cluster state of the elected master node
+<2> Timeout to connect to the master node as a `TimeValue`
+<3> Timeout to connect to the master node as a `String`
+
+include::../execution.asciidoc[]
+
+[id="{upid}-{api}-response"]
+==== Response
+
+The response is a +{response}+ value, `true` if any of the request's template
+names match existing templates and `false` otherwise

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

@@ -120,6 +120,11 @@ Alias Management::
 * <<{upid}-exists-alias>>
 * <<{upid}-get-alias>>
 
+Template Management::
+* <<{upid}-get-templates>>
+* <<{upid}-templates-exist>>
+* <<{upid}-put-template>>
+
 include::indices/analyze.asciidoc[]
 include::indices/create_index.asciidoc[]
 include::indices/delete_index.asciidoc[]
@@ -145,6 +150,7 @@ include::indices/get_settings.asciidoc[]
 include::indices/put_template.asciidoc[]
 include::indices/validate_query.asciidoc[]
 include::indices/get_templates.asciidoc[]
+include::indices/templates_exist.asciidoc[]
 include::indices/get_index.asciidoc[]
 include::indices/freeze_index.asciidoc[]
 include::indices/unfreeze_index.asciidoc[]