From afa346795744aab4ac90ba2199e471db87efe2b8 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Thu, 9 Apr 2020 07:43:13 +0200 Subject: [PATCH] [7.x] HLRC support for Index Templates V2 (#54838) (#54932) * HLRC support for Index Templates V2 (#54838) * HLRC support for Index Templates V2 This change adds High Level Rest Client support for Index Templates V2. Relates to #53101 * fixed compilation error Co-authored-by: Elastic Machine --- .../elasticsearch/client/IndicesClient.java | 127 +++++++++++++++++- .../client/IndicesRequestConverters.java | 57 ++++++++ .../indices/DeleteIndexTemplateV2Request.java | 35 +++++ .../indices/GetIndexTemplateV2Request.java | 80 +++++++++++ .../indices/GetIndexTemplatesV2Response.java | 108 +++++++++++++++ .../indices/IndexTemplateV2ExistRequest.java | 40 ++++++ .../indices/PutIndexTemplateV2Request.java | 100 ++++++++++++++ .../elasticsearch/client/IndicesClientIT.java | 54 ++++++++ .../client/RestHighLevelClientTests.java | 2 - .../GetComponentTemplatesResponseTests.java | 52 +++---- .../GetIndexTemplatesV2ResponseTests.java | 89 ++++++++++++ .../api/indices.exists_index_template.json | 39 ++++++ .../cluster/metadata/IndexTemplateV2.java | 2 +- 13 files changed, 757 insertions(+), 28 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/indices/DeleteIndexTemplateV2Request.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplateV2Request.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplatesV2Response.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/indices/IndexTemplateV2ExistRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/indices/PutIndexTemplateV2Request.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetIndexTemplatesV2ResponseTests.java create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_index_template.json diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java index 7b1e3f1c245..a0b83cb36ee 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java @@ -49,6 +49,7 @@ import org.elasticsearch.client.indices.CloseIndexResponse; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; import org.elasticsearch.client.indices.DeleteAliasRequest; +import org.elasticsearch.client.indices.DeleteIndexTemplateV2Request; import org.elasticsearch.client.indices.FreezeIndexRequest; import org.elasticsearch.client.indices.GetFieldMappingsRequest; import org.elasticsearch.client.indices.GetFieldMappingsResponse; @@ -56,10 +57,14 @@ import org.elasticsearch.client.indices.GetIndexRequest; import org.elasticsearch.client.indices.GetIndexResponse; import org.elasticsearch.client.indices.GetIndexTemplatesRequest; import org.elasticsearch.client.indices.GetIndexTemplatesResponse; +import org.elasticsearch.client.indices.GetIndexTemplateV2Request; +import org.elasticsearch.client.indices.GetIndexTemplatesV2Response; import org.elasticsearch.client.indices.GetMappingsRequest; import org.elasticsearch.client.indices.GetMappingsResponse; +import org.elasticsearch.client.indices.IndexTemplateV2ExistRequest; import org.elasticsearch.client.indices.IndexTemplatesExistRequest; import org.elasticsearch.client.indices.PutIndexTemplateRequest; +import org.elasticsearch.client.indices.PutIndexTemplateV2Request; import org.elasticsearch.client.indices.PutMappingRequest; import org.elasticsearch.client.indices.ReloadAnalyzersRequest; import org.elasticsearch.client.indices.ReloadAnalyzersResponse; @@ -1289,6 +1294,36 @@ public final class IndicesClient { AcknowledgedResponse::fromXContent, listener, emptySet()); } + /** + * Puts an index template using the Index Templates API. + * See Index Templates API + * on elastic.co + * @param putIndexTemplateRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public AcknowledgedResponse putIndexTemplate(PutIndexTemplateV2Request putIndexTemplateRequest, RequestOptions options) + throws IOException { + return restHighLevelClient.performRequestAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putIndexTemplate, + options, AcknowledgedResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously puts an index template using the Index Templates API. + * See Index Templates API + * on elastic.co + * @param putIndexTemplateRequest 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 + * @return cancellable that may be used to cancel the request + */ + public Cancellable putIndexTemplateAsync(PutIndexTemplateV2Request putIndexTemplateRequest, + RequestOptions options, ActionListener listener) { + return restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putIndexTemplate, + options, AcknowledgedResponse::fromXContent, listener, emptySet()); + } + /** * Validate a potentially expensive query without executing it. *

@@ -1344,6 +1379,36 @@ public final class IndicesClient { org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent, emptySet()); } + /** + * Gets index templates using the Index Templates API + * See Index Templates API + * on elastic.co + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param getIndexTemplatesRequest the request + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetIndexTemplatesV2Response getIndexTemplate(GetIndexTemplateV2Request getIndexTemplatesRequest, + RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest, IndicesRequestConverters::getIndexTemplates, + options, GetIndexTemplatesV2Response::fromXContent, emptySet()); + } + + /** + * Asynchronously gets index templates using the Index Templates API + * See Index Templates API + * on elastic.co + * @param getIndexTemplatesRequest 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 + * @return cancellable that may be used to cancel the request + */ + public Cancellable getIndexTemplateAsync(GetIndexTemplateV2Request getIndexTemplatesRequest, RequestOptions options, + ActionListener listener) { + return restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest, + IndicesRequestConverters::getIndexTemplates, options, GetIndexTemplatesV2Response::fromXContent, listener, emptySet()); + } + /** * Gets index templates using the Index Templates API * See Index Templates API @@ -1354,7 +1419,7 @@ public final class IndicesClient { * @throws IOException in case there is a problem sending the request or parsing back the response */ public GetIndexTemplatesResponse getIndexTemplate(GetIndexTemplatesRequest getIndexTemplatesRequest, - RequestOptions options) throws IOException { + RequestOptions options) throws IOException { return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest, IndicesRequestConverters::getTemplates, options, GetIndexTemplatesResponse::fromXContent, emptySet()); @@ -1429,6 +1494,37 @@ public final class IndicesClient { RestHighLevelClient::convertExistsResponse, 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 existsIndexTemplate(IndexTemplateV2ExistRequest 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} + * @return cancellable that may be used to cancel the request + */ + public Cancellable existsIndexTemplateAsync(IndexTemplateV2ExistRequest indexTemplatesExistRequest, + RequestOptions options, + ActionListener listener) { + + return restHighLevelClient.performRequestAsync(indexTemplatesExistRequest, + IndicesRequestConverters::templatesExist, options, + RestHighLevelClient::convertExistsResponse, listener, emptySet()); + } + /** * Calls the analyze API * @@ -1535,6 +1631,35 @@ public final class IndicesClient { options, AcknowledgedResponse::fromXContent, listener, emptySet()); } + /** + * Delete an index template using the Index Templates API + * See Index Templates API + * on elastic.co + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public AcknowledgedResponse deleteIndexTemplate(DeleteIndexTemplateV2Request request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, IndicesRequestConverters::deleteIndexTemplate, + options, AcknowledgedResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously delete an index template using the Index Templates API + * See Index Templates API + * on elastic.co + * @param request 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 + * @return cancellable that may be used to cancel the request + */ + public Cancellable deleteIndexTemplateAsync(DeleteIndexTemplateV2Request request, RequestOptions options, + ActionListener listener) { + return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::deleteIndexTemplate, + options, AcknowledgedResponse::fromXContent, listener, emptySet()); + } + /** * Synchronously calls the _reload_search_analyzers API * diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java index 4e6c5b5b1f0..4fa4332e072 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesRequestConverters.java @@ -42,13 +42,17 @@ import org.elasticsearch.client.indices.AnalyzeRequest; import org.elasticsearch.client.indices.CloseIndexRequest; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.DeleteAliasRequest; +import org.elasticsearch.client.indices.DeleteIndexTemplateV2Request; import org.elasticsearch.client.indices.FreezeIndexRequest; import org.elasticsearch.client.indices.GetFieldMappingsRequest; import org.elasticsearch.client.indices.GetIndexRequest; import org.elasticsearch.client.indices.GetIndexTemplatesRequest; +import org.elasticsearch.client.indices.GetIndexTemplateV2Request; import org.elasticsearch.client.indices.GetMappingsRequest; +import org.elasticsearch.client.indices.IndexTemplateV2ExistRequest; import org.elasticsearch.client.indices.IndexTemplatesExistRequest; import org.elasticsearch.client.indices.PutIndexTemplateRequest; +import org.elasticsearch.client.indices.PutIndexTemplateV2Request; import org.elasticsearch.client.indices.PutMappingRequest; import org.elasticsearch.client.indices.ReloadAnalyzersRequest; import org.elasticsearch.client.indices.ResizeRequest; @@ -580,6 +584,23 @@ final class IndicesRequestConverters { return request; } + static Request putIndexTemplate(PutIndexTemplateV2Request putIndexTemplateRequest) throws IOException { + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_index_template") + .addPathPart(putIndexTemplateRequest.name()).build(); + Request request = new Request(HttpPut.METHOD_NAME, endpoint); + RequestConverters.Params params = new RequestConverters.Params(); + params.withMasterTimeout(putIndexTemplateRequest.masterNodeTimeout()); + if (putIndexTemplateRequest.create()) { + params.putParam("create", Boolean.TRUE.toString()); + } + if (Strings.hasText(putIndexTemplateRequest.cause())) { + params.putParam("cause", putIndexTemplateRequest.cause()); + } + request.addParameters(params.asMap()); + request.setEntity(RequestConverters.createEntity(putIndexTemplateRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); + return request; + } + static Request validateQuery(ValidateQueryRequest validateQueryRequest) throws IOException { String[] indices = validateQueryRequest.indices() == null ? Strings.EMPTY_ARRAY : validateQueryRequest.indices(); String[] types = validateQueryRequest.types() == null || indices.length <= 0 ? Strings.EMPTY_ARRAY : validateQueryRequest.types(); @@ -632,6 +653,19 @@ final class IndicesRequestConverters { return request; } + static Request getIndexTemplates(GetIndexTemplateV2Request getIndexTemplatesRequest) { + final String endpoint = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_index_template") + .addPathPart(getIndexTemplatesRequest.name()) + .build(); + final Request request = new Request(HttpGet.METHOD_NAME, endpoint); + final RequestConverters.Params params = new RequestConverters.Params(); + params.withLocal(getIndexTemplatesRequest.isLocal()); + params.withMasterTimeout(getIndexTemplatesRequest.getMasterNodeTimeout()); + request.addParameters(params.asMap()); + return request; + } + static Request templatesExist(IndexTemplatesExistRequest indexTemplatesExistRequest) { final String endpoint = new RequestConverters.EndpointBuilder() .addPathPartAsIs("_template") @@ -645,6 +679,19 @@ final class IndicesRequestConverters { return request; } + static Request templatesExist(IndexTemplateV2ExistRequest indexTemplatesExistRequest) { + final String endpoint = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_index_template") + .addPathPart(indexTemplatesExistRequest.name()) + .build(); + final Request request = new Request(HttpHead.METHOD_NAME, endpoint); + final RequestConverters.Params params = new RequestConverters.Params(); + params.withLocal(indexTemplatesExistRequest.isLocal()); + params.withMasterTimeout(indexTemplatesExistRequest.getMasterNodeTimeout()); + request.addParameters(params.asMap()); + return request; + } + static Request analyze(AnalyzeRequest request) throws IOException { RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder(); String index = request.index(); @@ -691,6 +738,16 @@ final class IndicesRequestConverters { return request; } + static Request deleteIndexTemplate(DeleteIndexTemplateV2Request deleteIndexTemplateRequest) { + String name = deleteIndexTemplateRequest.getName(); + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_index_template").addPathPart(name).build(); + Request request = new Request(HttpDelete.METHOD_NAME, endpoint); + RequestConverters.Params params = new RequestConverters.Params(); + params.withMasterTimeout(deleteIndexTemplateRequest.masterNodeTimeout()); + request.addParameters(params.asMap()); + return request; + } + static Request reloadAnalyzers(ReloadAnalyzersRequest reloadAnalyzersRequest) { String endpoint = RequestConverters.endpoint(reloadAnalyzersRequest.getIndices(), "_reload_search_analyzers"); Request request = new Request(HttpPost.METHOD_NAME, endpoint); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/DeleteIndexTemplateV2Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/DeleteIndexTemplateV2Request.java new file mode 100644 index 00000000000..cb757e0e584 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/DeleteIndexTemplateV2Request.java @@ -0,0 +1,35 @@ +/* + * 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; + +public class DeleteIndexTemplateV2Request extends TimedRequest { + + private final String name; + + public DeleteIndexTemplateV2Request(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplateV2Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplateV2Request.java new file mode 100644 index 00000000000..0cc8e2af3a7 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplateV2Request.java @@ -0,0 +1,80 @@ +/* + * 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.unit.TimeValue; + +/** + * A request to read the content of index templates + */ +public class GetIndexTemplateV2Request implements Validatable { + + private final String name; + + private TimeValue masterNodeTimeout = TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT; + private boolean local = false; + + /** + * Create a request to read the content of index template. If no template name is provided, all templates will + * be read + * + * @param name the name of template to read + */ + public GetIndexTemplateV2Request(String name) { + this.name = name; + } + + /** + * @return the name of index template this request is requesting + */ + public String name() { + return name; + } + + /** + * @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; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplatesV2Response.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplatesV2Response.java new file mode 100644 index 00000000000..c96f874087f --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/GetIndexTemplatesV2Response.java @@ -0,0 +1,108 @@ +/* + * 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.cluster.metadata.IndexTemplateV2; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + + +public class GetIndexTemplatesV2Response { + + public static final ParseField NAME = new ParseField("name"); + public static final ParseField INDEX_TEMPLATES = new ParseField("index_templates"); + public static final ParseField INDEX_TEMPLATE = new ParseField("index_template"); + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser, Void> PARSER = + new ConstructingObjectParser<>("index_templates", false, + a -> ((List) a[0]).stream().collect(Collectors.toMap(n -> n.name, n -> n.indexTemplate, + (n1, n2) -> n1, LinkedHashMap::new))); + + private static final ConstructingObjectParser INNER_PARSER = + new ConstructingObjectParser<>("named_index_template", false, + a -> new NamedIndexTemplate((String) a[0], (IndexTemplateV2) a[1])); + + static { + INNER_PARSER.declareString(ConstructingObjectParser.constructorArg(), NAME); + INNER_PARSER.declareObject(ConstructingObjectParser.constructorArg(), IndexTemplateV2.PARSER, INDEX_TEMPLATE); + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), INNER_PARSER, INDEX_TEMPLATES); + } + + private static class NamedIndexTemplate { + String name; + IndexTemplateV2 indexTemplate; + + private NamedIndexTemplate(String name, IndexTemplateV2 indexTemplate) { + this.name = name; + this.indexTemplate = indexTemplate; + } + } + + @Override + public String toString() { + return "GetIndexTemplatesResponse [indexTemplates=" + indexTemplates + "]"; + } + + private final Map indexTemplates; + + GetIndexTemplatesV2Response(Map indexTemplates) { + this.indexTemplates = Collections.unmodifiableMap(new LinkedHashMap<>(indexTemplates)); + } + + public Map getIndexTemplates() { + return indexTemplates; + } + + + public static GetIndexTemplatesV2Response fromXContent(XContentParser parser) throws IOException { + return new GetIndexTemplatesV2Response(PARSER.apply(parser, null)); + } + + @Override + public int hashCode() { + return Objects.hash(indexTemplates); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GetIndexTemplatesV2Response other = (GetIndexTemplatesV2Response) obj; + return Objects.equals(indexTemplates, other.indexTemplates); + } + + +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/IndexTemplateV2ExistRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/IndexTemplateV2ExistRequest.java new file mode 100644 index 00000000000..d803406b307 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/IndexTemplateV2ExistRequest.java @@ -0,0 +1,40 @@ +/* + * 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.common.Strings; + +/** + * A request to check for the existence of index templates + */ +public class IndexTemplateV2ExistRequest extends GetComponentTemplatesRequest { + + /** + * Create a request to check for the existence of index template. Name must be provided + * + * @param name the name of template to check for the existence of + */ + public IndexTemplateV2ExistRequest(String name) { + super(name); + if (Strings.isNullOrEmpty(name)) { + throw new IllegalArgumentException("must provide index template name"); + } + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/PutIndexTemplateV2Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/PutIndexTemplateV2Request.java new file mode 100644 index 00000000000..93c7db5eb01 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indices/PutIndexTemplateV2Request.java @@ -0,0 +1,100 @@ +/* + * 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.cluster.metadata.IndexTemplateV2; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; + +/** + * A request to create an index template. + */ +public class PutIndexTemplateV2Request extends TimedRequest implements ToXContentObject { + + private String name; + + private String cause = ""; + + private boolean create; + + private IndexTemplateV2 indexTemplate; + + /** + * Sets the name of the index template. + */ + public PutIndexTemplateV2Request name(String name) { + if (Strings.isNullOrEmpty(name)) { + throw new IllegalArgumentException("name cannot be null or empty"); + } + this.name = name; + return this; + } + + /** + * The name of the index template. + */ + public String name() { + return this.name; + } + + /** + * Set to {@code true} to force only creation, not an update of an index template. If it already + * exists, it will fail with an {@link IllegalArgumentException}. + */ + public PutIndexTemplateV2Request create(boolean create) { + this.create = create; + return this; + } + + public boolean create() { + return create; + } + + /** + * The index template to create. + */ + public PutIndexTemplateV2Request indexTemplate(IndexTemplateV2 indexTemplate) { + this.indexTemplate = indexTemplate; + return this; + } + + /** + * The cause for this index template creation. + */ + public PutIndexTemplateV2Request cause(String cause) { + this.cause = cause; + return this; + } + + public String cause() { + return this.cause; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + if (indexTemplate != null) { + indexTemplate.toXContent(builder, params); + } + return builder; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java index 418caad87b4..f157624cc23 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java @@ -62,6 +62,7 @@ import org.elasticsearch.client.indices.CloseIndexResponse; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; import org.elasticsearch.client.indices.DeleteAliasRequest; +import org.elasticsearch.client.indices.DeleteIndexTemplateV2Request; import org.elasticsearch.client.indices.FreezeIndexRequest; import org.elasticsearch.client.indices.GetFieldMappingsRequest; import org.elasticsearch.client.indices.GetFieldMappingsResponse; @@ -69,11 +70,15 @@ import org.elasticsearch.client.indices.GetIndexRequest; import org.elasticsearch.client.indices.GetIndexResponse; import org.elasticsearch.client.indices.GetIndexTemplatesRequest; import org.elasticsearch.client.indices.GetIndexTemplatesResponse; +import org.elasticsearch.client.indices.GetIndexTemplateV2Request; +import org.elasticsearch.client.indices.GetIndexTemplatesV2Response; import org.elasticsearch.client.indices.GetMappingsRequest; import org.elasticsearch.client.indices.GetMappingsResponse; import org.elasticsearch.client.indices.IndexTemplateMetadata; +import org.elasticsearch.client.indices.IndexTemplateV2ExistRequest; import org.elasticsearch.client.indices.IndexTemplatesExistRequest; import org.elasticsearch.client.indices.PutIndexTemplateRequest; +import org.elasticsearch.client.indices.PutIndexTemplateV2Request; import org.elasticsearch.client.indices.PutMappingRequest; import org.elasticsearch.client.indices.ReloadAnalyzersRequest; import org.elasticsearch.client.indices.ReloadAnalyzersResponse; @@ -82,10 +87,13 @@ import org.elasticsearch.client.indices.rollover.RolloverRequest; import org.elasticsearch.client.indices.rollover.RolloverResponse; import org.elasticsearch.cluster.metadata.AliasMetadata; import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.IndexTemplateV2; import org.elasticsearch.cluster.metadata.MappingMetadata; +import org.elasticsearch.cluster.metadata.Template; import org.elasticsearch.common.Strings; import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; @@ -2021,4 +2029,50 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase { assertThat(aliasExists(index, alias), equalTo(false)); assertThat(aliasExists(index, alias2), equalTo(true)); } + + public void testIndexTemplates() throws Exception { + String templateName = "my-template"; + Settings settings = Settings.builder().put("index.number_of_shards", 1).build(); + CompressedXContent mappings = new CompressedXContent("{\"properties\":{\"host_name\":{\"type\":\"keyword\"}}}"); + AliasMetadata alias = AliasMetadata.builder("alias").writeIndex(true).build(); + Template template = new Template(settings, mappings, Collections.singletonMap("alias", alias)); + List pattern = Collections.singletonList("pattern"); + IndexTemplateV2 indexTemplate = new IndexTemplateV2(pattern, template, Collections.emptyList(), 1L, 1L, new HashMap<>()); + PutIndexTemplateV2Request putIndexTemplateV2Request = + new PutIndexTemplateV2Request().name(templateName).create(true).indexTemplate(indexTemplate); + + AcknowledgedResponse response = execute(putIndexTemplateV2Request, + highLevelClient().indices()::putIndexTemplate, highLevelClient().indices()::putIndexTemplateAsync); + assertThat(response.isAcknowledged(), equalTo(true)); + + IndexTemplateV2ExistRequest indexTemplateV2ExistRequest = new IndexTemplateV2ExistRequest(templateName); + boolean exist = execute(indexTemplateV2ExistRequest, + highLevelClient().indices()::existsIndexTemplate, highLevelClient().indices()::existsIndexTemplateAsync); + + assertTrue(exist); + + GetIndexTemplateV2Request getIndexTemplateV2Request = new GetIndexTemplateV2Request(templateName); + GetIndexTemplatesV2Response getResponse = execute(getIndexTemplateV2Request, + highLevelClient().indices()::getIndexTemplate, highLevelClient().indices()::getIndexTemplateAsync); + + assertThat(getResponse.getIndexTemplates().size(), equalTo(1)); + assertThat(getResponse.getIndexTemplates().containsKey(templateName), equalTo(true)); + assertThat(getResponse.getIndexTemplates().get(templateName), equalTo(indexTemplate)); + + DeleteIndexTemplateV2Request deleteIndexTemplateV2Request = new DeleteIndexTemplateV2Request(templateName); + response = execute(deleteIndexTemplateV2Request, highLevelClient().indices()::deleteIndexTemplate, + highLevelClient().indices()::deleteIndexTemplateAsync); + assertThat(response.isAcknowledged(), equalTo(true)); + + ElasticsearchStatusException statusException = expectThrows(ElasticsearchStatusException.class, + () -> execute(getIndexTemplateV2Request, + highLevelClient().indices()::getIndexTemplate, highLevelClient().indices()::getIndexTemplateAsync)); + + assertThat(statusException.status(), equalTo(RestStatus.NOT_FOUND)); + + exist = execute(indexTemplateV2ExistRequest, + highLevelClient().indices()::existsIndexTemplate, highLevelClient().indices()::existsIndexTemplateAsync); + + assertFalse(exist); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java index 4b505671ef4..d1a32c93ffd 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java @@ -861,8 +861,6 @@ public class RestHighLevelClientTests extends ESTestCase { "indices.create_data_stream", "indices.get_data_streams", "indices.delete_data_stream", - "indices.put_index_template", - "indices.delete_index_template" }; //These API are not required for high-level client feature completeness String[] notRequiredApi = new String[] { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetComponentTemplatesResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetComponentTemplatesResponseTests.java index 1f4acce23a6..ca3a5fcf8ee 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetComponentTemplatesResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetComponentTemplatesResponseTests.java @@ -47,12 +47,37 @@ public class GetComponentTemplatesResponseTests extends ESTestCase { .test(); } + public static Template randomTemplate() { + Settings settings = null; + CompressedXContent mappings = null; + Map aliases = null; + if (randomBoolean()) { + settings = randomSettings(); + } + if (randomBoolean()) { + mappings = randomMappings(); + } + if (randomBoolean()) { + aliases = randomAliases(); + } + return new Template(settings, mappings, aliases); + } + + public static Map randomMeta() { + if (randomBoolean()) { + return Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4)); + } else { + return Collections.singletonMap(randomAlphaOfLength(5), + Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4))); + } + } + private static GetComponentTemplatesResponse createTestInstance() { Map templates = new HashMap<>(); if (randomBoolean()) { int count = randomInt(10); for (int i = 0; i < count; i++) { - templates.put(randomAlphaOfLength(10), randomTemplate()); + templates.put(randomAlphaOfLength(10), randomComponentTemplate()); } } return new GetComponentTemplatesResponse(templates); @@ -72,20 +97,8 @@ public class GetComponentTemplatesResponseTests extends ESTestCase { builder.endObject(); } - private static ComponentTemplate randomTemplate() { - Settings settings = null; - CompressedXContent mappings = null; - Map aliases = null; - if (randomBoolean()) { - settings = randomSettings(); - } - if (randomBoolean()) { - mappings = randomMappings(); - } - if (randomBoolean()) { - aliases = randomAliases(); - } - Template template = new Template(settings, mappings, aliases); + private static ComponentTemplate randomComponentTemplate() { + Template template = randomTemplate(); Map meta = null; if (randomBoolean()) { @@ -119,13 +132,4 @@ public class GetComponentTemplatesResponseTests extends ESTestCase { .put(randomAlphaOfLength(4), randomAlphaOfLength(10)) .build(); } - - private static Map randomMeta() { - if (randomBoolean()) { - return Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4)); - } else { - return Collections.singletonMap(randomAlphaOfLength(5), - Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4))); - } - } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetIndexTemplatesV2ResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetIndexTemplatesV2ResponseTests.java new file mode 100644 index 00000000000..f5977cf6901 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indices/GetIndexTemplatesV2ResponseTests.java @@ -0,0 +1,89 @@ +/* + * 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.cluster.metadata.IndexTemplateV2; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.client.indices.GetComponentTemplatesResponseTests.randomMeta; +import static org.elasticsearch.client.indices.GetComponentTemplatesResponseTests.randomTemplate; +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; + +public class GetIndexTemplatesV2ResponseTests extends ESTestCase { + + public void testFromXContent() throws Exception { + xContentTester( + this::createParser, + GetIndexTemplatesV2ResponseTests::createTestInstance, + GetIndexTemplatesV2ResponseTests::toXContent, + GetIndexTemplatesV2Response::fromXContent) + .supportsUnknownFields(true) + .randomFieldsExcludeFilter(a -> true) + .test(); + } + + private static GetIndexTemplatesV2Response createTestInstance() { + Map templates = new HashMap<>(); + if (randomBoolean()) { + int count = randomInt(10); + for (int i = 0; i < count; i++) { + templates.put(randomAlphaOfLength(10), randomIndexTemplate()); + } + } + return new GetIndexTemplatesV2Response(templates); + } + + private static void toXContent(GetIndexTemplatesV2Response response, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.startArray("index_templates"); + for (Map.Entry e : response.getIndexTemplates().entrySet()) { + builder.startObject(); + builder.field("name", e.getKey()); + builder.field("index_template"); + e.getValue().toXContent(builder, null); + builder.endObject(); + } + builder.endArray(); + builder.endObject(); + } + + private static IndexTemplateV2 randomIndexTemplate() { + List patterns = Arrays.asList(generateRandomStringArray(10, 10, false, false)); + List composedOf = null; + Map meta = null; + if (randomBoolean()) { + composedOf = Arrays.asList(generateRandomStringArray(10, 10, false, false)); + } + if (randomBoolean()) { + meta = randomMeta(); + } + + Long priority = randomBoolean() ? null : randomNonNegativeLong(); + Long version = randomBoolean() ? null : randomNonNegativeLong(); + return new IndexTemplateV2(patterns, randomTemplate(), composedOf, priority, version, meta); + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_index_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_index_template.json new file mode 100644 index 00000000000..99471c50dee --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_index_template.json @@ -0,0 +1,39 @@ +{ + "indices.exists_index_template":{ + "documentation":{ + "url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html", + "description":"Returns information about whether a particular index template exists." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/_index_template/{name}", + "methods":[ + "HEAD" + ], + "parts":{ + "name":{ + "type":"string", + "description":"The name of the template" + } + } + } + ] + }, + "params":{ + "flat_settings":{ + "type":"boolean", + "description":"Return settings in flat format (default: false)" + }, + "master_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to master node" + }, + "local":{ + "type":"boolean", + "description":"Return local information, do not retrieve the state from master node (default: false)" + } + } + } +} diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateV2.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateV2.java index c91838e01a7..b77366c8ce1 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateV2.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexTemplateV2.java @@ -51,7 +51,7 @@ public class IndexTemplateV2 extends AbstractDiffable implement private static final ParseField METADATA = new ParseField("_meta"); @SuppressWarnings("unchecked") - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("index_template", false, + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("index_template", false, a -> new IndexTemplateV2((List) a[0], (Template) a[1], (List) a[2],