Add put index template api to high level rest client (#30400)

Relates #27205
This commit is contained in:
Nhat Nguyen 2018-05-06 09:47:36 -04:00 committed by GitHub
parent b46d01d409
commit eed8a3b585
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 590 additions and 16 deletions

View File

@ -51,6 +51,8 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -456,4 +458,26 @@ public final class IndicesClient {
UpdateSettingsResponse::fromXContent, listener, emptySet(), headers); UpdateSettingsResponse::fromXContent, listener, emptySet(), headers);
} }
/**
* Puts an index template using the Index Templates API
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
*/
public PutIndexTemplateResponse putTemplate(PutIndexTemplateRequest putIndexTemplateRequest, Header... headers) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(putIndexTemplateRequest, RequestConverters::putTemplate,
PutIndexTemplateResponse::fromXContent, emptySet(), headers);
}
/**
* Asynchronously puts an index template using the Index Templates API
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
*/
public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest,
ActionListener<PutIndexTemplateResponse> listener, Header... headers) {
restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, RequestConverters::putTemplate,
PutIndexTemplateResponse::fromXContent, listener, emptySet(), headers);
}
} }

View File

@ -47,6 +47,7 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType; import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest; import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
@ -77,7 +78,6 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.VersionType; import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.rankeval.RankEvalRequest; import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.rest.action.RestFieldCapabilitiesAction;
import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
@ -86,10 +86,7 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner; import java.util.StringJoiner;
final class RequestConverters { final class RequestConverters {
@ -647,6 +644,21 @@ final class RequestConverters {
return request; return request;
} }
static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) throws IOException {
String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addPathPart(putIndexTemplateRequest.name()).build();
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
Params params = new Params(request);
params.withMasterTimeout(putIndexTemplateRequest.masterNodeTimeout());
if (putIndexTemplateRequest.create()) {
params.putParam("create", Boolean.TRUE.toString());
}
if (Strings.hasText(putIndexTemplateRequest.cause())) {
params.putParam("cause", putIndexTemplateRequest.cause());
}
request.setEntity(createEntity(putIndexTemplateRequest, REQUEST_BODY_CONTENT_TYPE));
return request;
}
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException { private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef(); BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType)); return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));

View File

@ -56,11 +56,14 @@ import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType; import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.broadcast.BroadcastResponse; import org.elasticsearch.action.support.broadcast.BroadcastResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ValidationException;
import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeUnit;
@ -73,11 +76,19 @@ import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractRawValues;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
@ -812,4 +823,59 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
+ "or check the breaking changes documentation for removed settings]")); + "or check the breaking changes documentation for removed settings]"));
} }
@SuppressWarnings("unchecked")
public void testPutTemplate() throws Exception {
PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest()
.name("my-template")
.patterns(Arrays.asList("pattern-1", "name-*"))
.order(10)
.create(randomBoolean())
.settings(Settings.builder().put("number_of_shards", "3").put("number_of_replicas", "0"))
.mapping("doc", "host_name", "type=keyword", "description", "type=text")
.alias(new Alias("alias-1").indexRouting("abc")).alias(new Alias("{index}-write").searchRouting("xyz"));
PutIndexTemplateResponse putTemplateResponse = execute(putTemplateRequest,
highLevelClient().indices()::putTemplate, highLevelClient().indices()::putTemplateAsync);
assertThat(putTemplateResponse.isAcknowledged(), equalTo(true));
Map<String, Object> templates = getAsMap("/_template/my-template");
assertThat(templates.keySet(), hasSize(1));
assertThat(extractValue("my-template.order", templates), equalTo(10));
assertThat(extractRawValues("my-template.index_patterns", templates), contains("pattern-1", "name-*"));
assertThat(extractValue("my-template.settings.index.number_of_shards", templates), equalTo("3"));
assertThat(extractValue("my-template.settings.index.number_of_replicas", templates), equalTo("0"));
assertThat(extractValue("my-template.mappings.doc.properties.host_name.type", templates), equalTo("keyword"));
assertThat(extractValue("my-template.mappings.doc.properties.description.type", templates), equalTo("text"));
assertThat((Map<String, String>) extractValue("my-template.aliases.alias-1", templates), hasEntry("index_routing", "abc"));
assertThat((Map<String, String>) extractValue("my-template.aliases.{index}-write", templates), hasEntry("search_routing", "xyz"));
}
public void testPutTemplateBadRequests() throws Exception {
RestHighLevelClient client = highLevelClient();
// Failed to validate because index patterns are missing
PutIndexTemplateRequest withoutPattern = new PutIndexTemplateRequest("t1");
ValidationException withoutPatternError = expectThrows(ValidationException.class,
() -> execute(withoutPattern, client.indices()::putTemplate, client.indices()::putTemplateAsync));
assertThat(withoutPatternError.validationErrors(), contains("index patterns are missing"));
// Create-only specified but an template exists already
PutIndexTemplateRequest goodTemplate = new PutIndexTemplateRequest("t2").patterns(Arrays.asList("qa-*", "prod-*"));
assertTrue(execute(goodTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged());
goodTemplate.create(true);
ElasticsearchException alreadyExistsError = expectThrows(ElasticsearchException.class,
() -> execute(goodTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync));
assertThat(alreadyExistsError.getDetailedMessage(),
containsString("[type=illegal_argument_exception, reason=index_template [t2] already exists]"));
goodTemplate.create(false);
assertTrue(execute(goodTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged());
// Rejected due to unknown settings
PutIndexTemplateRequest unknownSettingTemplate = new PutIndexTemplateRequest("t3")
.patterns(Collections.singletonList("any"))
.settings(Settings.builder().put("this-setting-does-not-exist", 100));
ElasticsearchStatusException unknownSettingError = expectThrows(ElasticsearchStatusException.class,
() -> execute(unknownSettingTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync));
assertThat(unknownSettingError.getDetailedMessage(), containsString("unknown setting [index.this-setting-does-not-exist]"));
}
} }

View File

@ -26,12 +26,11 @@ import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
@ -46,10 +45,11 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest; import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType; import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkShardRequest; import org.elasticsearch.action.bulk.BulkShardRequest;
import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteRequest;
@ -70,6 +70,7 @@ import org.elasticsearch.action.support.master.MasterNodeReadRequest;
import org.elasticsearch.action.support.master.MasterNodeRequest; import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.action.support.replication.ReplicationRequest; import org.elasticsearch.action.support.replication.ReplicationRequest;
import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestConverters.EndpointBuilder;
import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
@ -77,15 +78,13 @@ import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.lucene.uid.Versions;
import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.client.RequestConverters.EndpointBuilder;
import org.elasticsearch.client.RequestConverters.Params;
import org.elasticsearch.index.RandomCreateIndexGenerator; import org.elasticsearch.index.RandomCreateIndexGenerator;
import org.elasticsearch.index.VersionType; import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder;
@ -94,7 +93,6 @@ import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.index.rankeval.RankEvalSpec; import org.elasticsearch.index.rankeval.RankEvalSpec;
import org.elasticsearch.index.rankeval.RatedRequest; import org.elasticsearch.index.rankeval.RatedRequest;
import org.elasticsearch.index.rankeval.RestRankEvalAction; import org.elasticsearch.index.rankeval.RestRankEvalAction;
import org.elasticsearch.rest.action.RestFieldCapabilitiesAction;
import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.search.Scroll; import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
@ -111,8 +109,6 @@ import org.elasticsearch.test.RandomObjects;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -121,7 +117,6 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -1432,6 +1427,48 @@ public class RequestConvertersTests extends ESTestCase {
assertEquals(expectedParams, request.getParameters()); assertEquals(expectedParams, request.getParameters());
} }
public void testPutTemplateRequest() throws Exception {
Map<String, String> names = new HashMap<>();
names.put("log", "log");
names.put("template#1", "template%231");
names.put("-#template", "-%23template");
names.put("foo^bar", "foo%5Ebar");
PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest()
.name(randomFrom(names.keySet()))
.patterns(Arrays.asList(generateRandomStringArray(20, 100, false, false)));
if (randomBoolean()) {
putTemplateRequest.order(randomInt());
}
if (randomBoolean()) {
putTemplateRequest.version(randomInt());
}
if (randomBoolean()) {
putTemplateRequest.settings(Settings.builder().put("setting-" + randomInt(), randomTimeValue()));
}
if (randomBoolean()) {
putTemplateRequest.mapping("doc-" + randomInt(), "field-" + randomInt(), "type=" + randomFrom("text", "keyword"));
}
if (randomBoolean()) {
putTemplateRequest.alias(new Alias("alias-" + randomInt()));
}
Map<String, String> expectedParams = new HashMap<>();
if (randomBoolean()) {
expectedParams.put("create", Boolean.TRUE.toString());
putTemplateRequest.create(true);
}
if (randomBoolean()) {
String cause = randomUnicodeOfCodepointLengthBetween(1, 50);
putTemplateRequest.cause(cause);
expectedParams.put("cause", cause);
}
setRandomMasterTimeout(putTemplateRequest, expectedParams);
Request request = RequestConverters.putTemplate(putTemplateRequest);
assertThat(request.getEndpoint(), equalTo("/_template/" + names.get(putTemplateRequest.name())));
assertThat(request.getParameters(), equalTo(expectedParams));
assertToXContentBody(putTemplateRequest, request.getEntity());
}
private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException { private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false); BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false);
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue()); assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());

View File

@ -55,6 +55,10 @@ import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType; import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.action.support.DefaultShardOperationFailedException; import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.IndicesOptions;
@ -71,11 +75,14 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.equalTo;
/** /**
* This class is used to generate the Java Indices API documentation. * This class is used to generate the Java Indices API documentation.
* You need to wrap your code between two tags like: * You need to wrap your code between two tags like:
@ -1598,4 +1605,164 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
assertTrue(latch.await(30L, TimeUnit.SECONDS)); assertTrue(latch.await(30L, TimeUnit.SECONDS));
} }
public void testPutTemplate() throws Exception {
RestHighLevelClient client = highLevelClient();
// tag::put-template-request
PutIndexTemplateRequest request = new PutIndexTemplateRequest("my-template"); // <1>
request.patterns(Arrays.asList("pattern-1", "log-*")); // <2>
// end::put-template-request
// tag::put-template-request-settings
request.settings(Settings.builder() // <1>
.put("index.number_of_shards", 3)
.put("index.number_of_replicas", 1)
);
// end::put-template-request-settings
{
// tag::create-put-template-request-mappings
request.mapping("tweet", // <1>
"{\n" +
" \"tweet\": {\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}", // <2>
XContentType.JSON);
// end::create-put-template-mappings
assertTrue(client.indices().putTemplate(request).isAcknowledged());
}
{
//tag::put-template-mappings-map
Map<String, Object> jsonMap = new HashMap<>();
Map<String, Object> message = new HashMap<>();
message.put("type", "text");
Map<String, Object> properties = new HashMap<>();
properties.put("message", message);
Map<String, Object> tweet = new HashMap<>();
tweet.put("properties", properties);
jsonMap.put("tweet", tweet);
request.mapping("tweet", jsonMap); // <1>
//end::put-template-mappings-map
assertTrue(client.indices().putTemplate(request).isAcknowledged());
}
{
//tag::put-template-mappings-xcontent
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.startObject("tweet");
{
builder.startObject("properties");
{
builder.startObject("message");
{
builder.field("type", "text");
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
request.mapping("tweet", builder); // <1>
//end::put-template-mappings-xcontent
assertTrue(client.indices().putTemplate(request).isAcknowledged());
}
{
//tag::put-template-mappings-shortcut
request.mapping("tweet", "message", "type=text"); // <1>
//end::put-template-mappings-shortcut
assertTrue(client.indices().putTemplate(request).isAcknowledged());
}
// tag::put-template-request-aliases
request.alias(new Alias("twitter_alias").filter(QueryBuilders.termQuery("user", "kimchy"))); // <1>
request.alias(new Alias("{index}_alias").searchRouting("xyz")); // <2>
// end::put-template-request-aliases
// tag::put-template-request-order
request.order(20); // <1>
// end::put-template-request-order
// tag::put-template-request-order
request.version(4); // <1>
// end::put-template-request-order
// tag::put-template-whole-source
request.source("{\n" +
" \"index_patterns\": [\n" +
" \"log-*\",\n" +
" \"pattern-1\"\n" +
" ],\n" +
" \"order\": 1,\n" +
" \"settings\": {\n" +
" \"number_of_shards\": 1\n" +
" },\n" +
" \"mappings\": {\n" +
" \"tweet\": {\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" }\n" +
" }\n" +
" },\n" +
" \"aliases\": {\n" +
" \"alias-1\": {},\n" +
" \"{index}-alias\": {}\n" +
" }\n" +
"}", XContentType.JSON); // <1>
// end::put-template-whole-source
// tag::put-template-request-create
request.create(true); // <1>
// end::put-template-request-create
// tag::put-template-request-masterTimeout
request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
request.masterNodeTimeout("1m"); // <2>
// end::put-template-request-masterTimeout
request.create(false); // make test happy
// tag::put-template-execute
PutIndexTemplateResponse putTemplateResponse = client.indices().putTemplate(request);
// end::put-template-execute
// tag::put-template-response
boolean acknowledged = putTemplateResponse.isAcknowledged(); // <1>
// end::put-template-response
assertTrue(acknowledged);
// tag::put-template-execute-listener
ActionListener<PutIndexTemplateResponse> listener =
new ActionListener<PutIndexTemplateResponse>() {
@Override
public void onResponse(PutIndexTemplateResponse putTemplateResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::put-template-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-template-execute-async
client.indices().putTemplateAsync(request, listener); // <1>
// end::put-template-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
} }

View File

@ -163,6 +163,8 @@ synchronous and predictable. Also the trigger engine thread is only started on
data nodes. And the Execute Watch API can be triggered regardless is watcher is data nodes. And the Execute Watch API can be triggered regardless is watcher is
started or stopped. ({pull}30118[#30118]) started or stopped. ({pull}30118[#30118])
Added put index template API to the high level rest client ({pull}30400[#30400])
[float] [float]
=== Bug Fixes === Bug Fixes

View File

@ -0,0 +1,168 @@
[[java-rest-high-put-template]]
=== Put Template API
[[java-rest-high-put-template-request]]
==== Put Index Template Request
A `PutIndexTemplateRequest` specifies the `name` of a template and `patterns`
which controls whether the template should be applied to the new index.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request]
--------------------------------------------------
<1> The name of the template
<2> The patterns of the template
==== Settings
The settings of the template will be applied to the new index whose name matches the
template's patterns.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-settings]
--------------------------------------------------
<1> Settings for this template
[[java-rest-high-put-template-request-mappings]]
==== Mappings
The mapping of the template will be applied to the new index whose name matches the
template's patterns.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-mappings]
--------------------------------------------------
<1> The type to define
<2> The mapping for this type, provided as a JSON string
The mapping source can be provided in different ways in addition to the
`String` example shown above:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-mappings-map]
--------------------------------------------------
<1> Mapping source provided as a `Map` which gets automatically converted
to JSON format
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-mappings-xcontent]
--------------------------------------------------
<1> Mapping source provided as an `XContentBuilder` object, the Elasticsearch
built-in helpers to generate JSON content
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-mappings-shortcut]
--------------------------------------------------
<1> Mapping source provided as `Object` key-pairs, which gets converted to
JSON format
==== Aliases
The aliases of the template will define aliasing to the index whose name matches the
template's patterns. A placeholder `{index}` can be used in an alias of a template.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-aliases]
--------------------------------------------------
<1> The alias to define
<2> The alias to define with placeholder
==== Order
In case multiple templates match an index, the orders of matching templates determine
the sequence that settings, mappings, and alias of each matching template is applied.
Templates with lower orders are applied first, and higher orders override them.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-order]
--------------------------------------------------
<1> The order of the template
==== Version
A template can optionally specify a version number which can be used to simplify template
management by external systems.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-version]
--------------------------------------------------
<1> The version number of the template
==== Providing the whole source
The whole source including all of its sections (mappings, settings and aliases)
can also be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-whole-source]
--------------------------------------------------
<1> The source provided as a JSON string. It can also be provided as a `Map`
or an `XContentBuilder`.
==== Optional arguments
The following arguments can optionally be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-create]
--------------------------------------------------
<1> To force to only create a new template; do not overwrite the existing template
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-masterTimeout]
--------------------------------------------------
<1> Timeout to connect to the master node as a `TimeValue`
<2> Timeout to connect to the master node as a `String`
[[java-rest-high-put-template-sync]]
==== Synchronous Execution
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-execute]
--------------------------------------------------
[[java-rest-high-put-template-async]]
==== Asynchronous Execution
The asynchronous execution of a put template request requires both the `PutIndexTemplateRequest`
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-template-execute-async]
--------------------------------------------------
<1> The `PutIndexTemplateRequest` 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 `PutIndexTemplateResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of failure. The raised exception is provided as an argument
[[java-rest-high-put-template-response]]
==== Put Index Template Response
The returned `PutIndexTemplateResponse` allows to retrieve information about the
executed operation as follows:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-response]
--------------------------------------------------
<1> Indicates whether all of the nodes have acknowledged the request

View File

@ -95,6 +95,7 @@ include::indices/update_aliases.asciidoc[]
include::indices/exists_alias.asciidoc[] include::indices/exists_alias.asciidoc[]
include::indices/put_settings.asciidoc[] include::indices/put_settings.asciidoc[]
include::indices/get_settings.asciidoc[] include::indices/get_settings.asciidoc[]
include::indices/put_template.asciidoc[]
== Cluster APIs == Cluster APIs

View File

@ -39,6 +39,7 @@ import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
@ -58,14 +59,14 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.elasticsearch.action.ValidateActions.addValidationError; 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.readSettingsFromStream;
import static org.elasticsearch.common.settings.Settings.writeSettingsToStream; import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
/** /**
* A request to create an index template. * A request to create an index template.
*/ */
public class PutIndexTemplateRequest extends MasterNodeRequest<PutIndexTemplateRequest> implements IndicesRequest { public class PutIndexTemplateRequest extends MasterNodeRequest<PutIndexTemplateRequest> implements IndicesRequest, ToXContent {
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(PutIndexTemplateRequest.class)); private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(PutIndexTemplateRequest.class));
@ -539,4 +540,34 @@ public class PutIndexTemplateRequest extends MasterNodeRequest<PutIndexTemplateR
} }
out.writeOptionalVInt(version); out.writeOptionalVInt(version);
} }
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (customs.isEmpty() == false) {
throw new IllegalArgumentException("Custom data type is no longer supported in index template [" + customs + "]");
}
builder.field("index_patterns", indexPatterns);
builder.field("order", order);
if (version != null) {
builder.field("version", version);
}
builder.startObject("settings");
settings.toXContent(builder, params);
builder.endObject();
builder.startObject("mappings");
for (Map.Entry<String, String> entry : mappings.entrySet()) {
Map<String, Object> mapping = XContentHelper.convertToMap(new BytesArray(entry.getValue()), false).v2();
builder.field(entry.getKey(), mapping);
}
builder.endObject();
builder.startObject("aliases");
for (Alias alias : aliases) {
alias.toXContent(builder, params);
}
builder.endObject();
return builder;
}
} }

View File

@ -21,6 +21,8 @@ package org.elasticsearch.action.admin.indices.template.put;
import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
@ -47,4 +49,14 @@ public class PutIndexTemplateResponse extends AcknowledgedResponse {
super.writeTo(out); super.writeTo(out);
writeAcknowledged(out); writeAcknowledged(out);
} }
private static final ConstructingObjectParser<PutIndexTemplateResponse, Void> PARSER;
static {
PARSER = new ConstructingObjectParser<>("put_index_template", true, args -> new PutIndexTemplateResponse((boolean) args[0]));
declareAcknowledgedField(PARSER);
}
public static PutIndexTemplateResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
} }

View File

@ -20,10 +20,15 @@ package org.elasticsearch.action.admin.indices.template.put;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.yaml.YamlXContent; import org.elasticsearch.common.xcontent.yaml.YamlXContent;
@ -35,6 +40,7 @@ import java.util.Base64;
import java.util.Collections; import java.util.Collections;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is; import static org.hamcrest.core.Is.is;
@ -131,4 +137,52 @@ public class PutIndexTemplateRequestTests extends ESTestCase {
assertThat(noError, is(nullValue())); assertThat(noError, is(nullValue()));
} }
private PutIndexTemplateRequest randomPutIndexTemplateRequest() throws IOException {
PutIndexTemplateRequest request = new PutIndexTemplateRequest();
request.name("test");
if (randomBoolean()){
request.version(randomInt());
}
if (randomBoolean()){
request.order(randomInt());
}
request.patterns(Arrays.asList(generateRandomStringArray(20, 100, false, false)));
int numAlias = between(0, 5);
for (int i = 0; i < numAlias; i++) {
Alias alias = new Alias(randomRealisticUnicodeOfLengthBetween(1, 10));
if (randomBoolean()) {
alias.indexRouting(randomRealisticUnicodeOfLengthBetween(1, 10));
}
if (randomBoolean()) {
alias.searchRouting(randomRealisticUnicodeOfLengthBetween(1, 10));
}
request.alias(alias);
}
if (randomBoolean()) {
request.mapping("doc", XContentFactory.jsonBuilder().startObject()
.startObject("doc").startObject("properties")
.startObject("field-" + randomInt()).field("type", randomFrom("keyword", "text")).endObject()
.endObject().endObject().endObject());
}
if (randomBoolean()){
request.settings(Settings.builder().put("setting1", randomLong()).put("setting2", randomTimeValue()).build());
}
return request;
}
public void testFromToXContentPutTemplateRequest() throws Exception {
for (int i = 0; i < 10; i++) {
PutIndexTemplateRequest expected = randomPutIndexTemplateRequest();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference shuffled = toShuffledXContent(expected, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
PutIndexTemplateRequest parsed = new PutIndexTemplateRequest().source(shuffled, xContentType);
assertNotSame(expected, parsed);
assertThat(parsed.version(), equalTo(expected.version()));
assertThat(parsed.order(), equalTo(expected.order()));
assertThat(parsed.patterns(), equalTo(expected.patterns()));
assertThat(parsed.aliases(), equalTo(expected.aliases()));
assertThat(parsed.mappings(), equalTo(expected.mappings()));
assertThat(parsed.settings(), equalTo(expected.settings()));
}
}
} }