Types removal - deprecate include_type_name with index templates (#37484)

Added deprecation warnings for use of include_type_name in put/get index templates.
HLRC changes:
GetIndexTemplateRequest has a new client-side class which is a copy of server's GetIndexTemplateResponse but modified to be typeless.
PutIndexTemplateRequest has a new client-side counterpart which doesn't use types in the mappings
Relates to #35190
This commit is contained in:
markharwood 2019-01-29 20:52:41 +00:00 committed by GitHub
parent 193017672a
commit b889221f75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1628 additions and 113 deletions

View File

@ -49,8 +49,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.GetIndexTemplatesResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
@ -61,7 +59,9 @@ import org.elasticsearch.client.indices.FreezeIndexRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.client.indices.UnfreezeIndexRequest;
import org.elasticsearch.rest.RestStatus;
@ -908,6 +908,45 @@ public final class IndicesClient {
AcknowledgedResponse::fromXContent, listener, emptySet());
}
/**
* Puts an index template using the Index Templates API.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
* @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
* @deprecated This old form of request allows types in mappings. Use {@link #putTemplate(PutIndexTemplateRequest, RequestOptions)}
* instead which introduces a new request object without types.
*/
@Deprecated
public AcknowledgedResponse putTemplate(
org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putIndexTemplateRequest,
RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putTemplate, options,
AcknowledgedResponse::fromXContent, emptySet());
}
/**
* Asynchronously puts an index template using the Index Templates API.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
* @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
* @deprecated This old form of request allows types in mappings.
* Use {@link #putTemplateAsync(PutIndexTemplateRequest, RequestOptions, ActionListener)}
* instead which introduces a new request object without types.
*/
@Deprecated
public void putTemplateAsync(org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putIndexTemplateRequest,
RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putTemplate, options,
AcknowledgedResponse::fromXContent, listener, emptySet());
}
/**
* Puts an index template using the Index Templates API.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
@ -917,8 +956,9 @@ public final class IndicesClient {
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public AcknowledgedResponse putTemplate(PutIndexTemplateRequest putIndexTemplateRequest,
RequestOptions options) throws IOException {
public AcknowledgedResponse putTemplate(
PutIndexTemplateRequest putIndexTemplateRequest,
RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putTemplate, options,
AcknowledgedResponse::fromXContent, emptySet());
}
@ -931,8 +971,8 @@ public final class IndicesClient {
* @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
*/
public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest, RequestOptions options,
ActionListener<AcknowledgedResponse> listener) {
public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest,
RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putTemplate, options,
AcknowledgedResponse::fromXContent, listener, emptySet());
}
@ -968,20 +1008,60 @@ public final class IndicesClient {
}
/**
* Gets index templates using the Index Templates API
* Gets index templates using the Index Templates API. The mappings will be returned in a legacy deprecated format, where the
* mapping definition is nested under the type name.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
* @param getIndexTemplatesRequest 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
* @deprecated This method uses an old response object which still refers to types, a deprecated feature. Use
* {@link #getIndexTemplate(GetIndexTemplatesRequest, RequestOptions)} instead which returns a new response object
*/
public GetIndexTemplatesResponse getTemplate(GetIndexTemplatesRequest getIndexTemplatesRequest,
RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest, IndicesRequestConverters::getTemplates,
options, GetIndexTemplatesResponse::fromXContent, emptySet());
@Deprecated
public org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse getTemplate(
GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest,
IndicesRequestConverters::getTemplatesWithDocumentTypes,
options, org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent, emptySet());
}
/**
* Gets index templates using the Index Templates API
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
* @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 GetIndexTemplatesResponse getIndexTemplate(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest,
IndicesRequestConverters::getTemplates,
options, GetIndexTemplatesResponse::fromXContent, emptySet());
}
/**
* Asynchronously gets index templates using the Index Templates API. The mappings will be returned in a legacy deprecated format,
* where the mapping definition is nested under the type name.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
* on elastic.co</a>
* @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
* @deprecated This method uses an old response object which still refers to types, a deprecated feature. Use
* {@link #getIndexTemplateAsync(GetIndexTemplatesRequest, RequestOptions, ActionListener)} instead which returns a new response object
*/
@Deprecated
public void getTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
ActionListener<org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest,
IndicesRequestConverters::getTemplatesWithDocumentTypes,
options, org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent, listener, emptySet());
}
/**
* Asynchronously gets index templates using the Index Templates API
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
@ -990,11 +1070,12 @@ public final class IndicesClient {
* @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
*/
public void getTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
public void getIndexTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
ActionListener<GetIndexTemplatesResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest, IndicesRequestConverters::getTemplates,
restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest,
IndicesRequestConverters::getTemplates,
options, GetIndexTemplatesResponse::fromXContent, listener, emptySet());
}
}
/**
* Uses the Index Templates API to determine if index templates exist

View File

@ -43,13 +43,13 @@ 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.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.FreezeIndexRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.client.indices.UnfreezeIndexRequest;
import org.elasticsearch.common.Strings;
@ -416,7 +416,13 @@ final class IndicesRequestConverters {
return request;
}
static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) throws IOException {
/**
* @deprecated This uses the old form of PutIndexTemplateRequest which uses types.
* Use (@link {@link #putTemplate(PutIndexTemplateRequest)} instead
*/
@Deprecated
static Request putTemplate(org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putIndexTemplateRequest)
throws IOException {
String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_template")
.addPathPart(putIndexTemplateRequest.name()).build();
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
@ -433,6 +439,22 @@ final class IndicesRequestConverters {
return request;
}
static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) throws IOException {
String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_template")
.addPathPart(putIndexTemplateRequest.name()).build();
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
RequestConverters.Params params = new RequestConverters.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(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();
@ -458,7 +480,16 @@ final class IndicesRequestConverters {
return request;
}
@Deprecated
static Request getTemplatesWithDocumentTypes(GetIndexTemplatesRequest getIndexTemplatesRequest) {
return getTemplates(getIndexTemplatesRequest, true);
}
static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) {
return getTemplates(getIndexTemplatesRequest, false);
}
private static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest, boolean includeTypeName) {
final String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_template")
.addCommaSeparatedPathParts(getIndexTemplatesRequest.names())
@ -467,9 +498,11 @@ final class IndicesRequestConverters {
final RequestConverters.Params params = new RequestConverters.Params(request);
params.withLocal(getIndexTemplatesRequest.isLocal());
params.withMasterTimeout(getIndexTemplatesRequest.getMasterNodeTimeout());
params.putParam(INCLUDE_TYPE_NAME_PARAMETER, "true");
if (includeTypeName) {
params.putParam(INCLUDE_TYPE_NAME_PARAMETER, "true");
}
return request;
}
}
static Request templatesExist(IndexTemplatesExistRequest indexTemplatesExistRequest) {
final String endpoint = new RequestConverters.EndpointBuilder()

View File

@ -0,0 +1,90 @@
/*
* 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.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
public class GetIndexTemplatesResponse {
@Override
public String toString() {
List<IndexTemplateMetaData> thisList = new ArrayList<>(this.indexTemplates);
thisList.sort(Comparator.comparing(IndexTemplateMetaData::name));
return "GetIndexTemplatesResponse [indexTemplates=" + thisList + "]";
}
private final List<IndexTemplateMetaData> indexTemplates;
GetIndexTemplatesResponse() {
indexTemplates = new ArrayList<>();
}
GetIndexTemplatesResponse(List<IndexTemplateMetaData> indexTemplates) {
this.indexTemplates = indexTemplates;
}
public List<IndexTemplateMetaData> getIndexTemplates() {
return indexTemplates;
}
public static GetIndexTemplatesResponse fromXContent(XContentParser parser) throws IOException {
final List<IndexTemplateMetaData> templates = new ArrayList<>();
for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) {
if (token == XContentParser.Token.FIELD_NAME) {
final IndexTemplateMetaData templateMetaData = IndexTemplateMetaData.Builder.fromXContent(parser, parser.currentName());
templates.add(templateMetaData);
}
}
return new GetIndexTemplatesResponse(templates);
}
@Override
public int hashCode() {
List<IndexTemplateMetaData> sortedList = new ArrayList<>(this.indexTemplates);
sortedList.sort(Comparator.comparing(IndexTemplateMetaData::name));
return Objects.hash(sortedList);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
// To compare results we need to make sure the templates are listed in the same order
GetIndexTemplatesResponse other = (GetIndexTemplatesResponse) obj;
List<IndexTemplateMetaData> thisList = new ArrayList<>(this.indexTemplates);
List<IndexTemplateMetaData> otherList = new ArrayList<>(other.indexTemplates);
thisList.sort(Comparator.comparing(IndexTemplateMetaData::name));
otherList.sort(Comparator.comparing(IndexTemplateMetaData::name));
return Objects.equals(thisList, otherList);
}
}

View File

@ -0,0 +1,300 @@
/*
* 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.ElasticsearchParseException;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.MapperService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class IndexTemplateMetaData {
private final String name;
private final int order;
/**
* The version is an arbitrary number managed by the user so that they can easily and quickly verify the existence of a given template.
* Expected usage:
* <pre><code>
* PUT /_template/my_template
* {
* "index_patterns": ["my_index-*"],
* "mappings": { ... },
* "version": 1
* }
* </code></pre>
* Then, some process from the user can occasionally verify that the template exists with the appropriate version without having to
* check the template's content:
* <pre><code>
* GET /_template/my_template?filter_path=*.version
* </code></pre>
*/
@Nullable
private final Integer version;
private final List<String> patterns;
private final Settings settings;
private final MappingMetaData mappings;
private final ImmutableOpenMap<String, AliasMetaData> aliases;
public IndexTemplateMetaData(String name, int order, Integer version,
List<String> patterns, Settings settings,
MappingMetaData mappings,
ImmutableOpenMap<String, AliasMetaData> aliases) {
if (patterns == null || patterns.isEmpty()) {
throw new IllegalArgumentException("Index patterns must not be null or empty; got " + patterns);
}
this.name = name;
this.order = order;
this.version = version;
this.patterns= patterns;
this.settings = settings;
this.mappings = mappings;
this.aliases = aliases;
}
public String name() {
return this.name;
}
public int order() {
return this.order;
}
@Nullable
public Integer version() {
return version;
}
public List<String> patterns() {
return this.patterns;
}
public Settings settings() {
return this.settings;
}
public MappingMetaData mappings() {
return this.mappings;
}
public ImmutableOpenMap<String, AliasMetaData> aliases() {
return this.aliases;
}
public static Builder builder(String name) {
return new Builder(name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IndexTemplateMetaData that = (IndexTemplateMetaData) o;
if (order != that.order) return false;
if (!Objects.equals(mappings, that.mappings)) return false;
if (!name.equals(that.name)) return false;
if (!settings.equals(that.settings)) return false;
if (!patterns.equals(that.patterns)) return false;
return Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(name, order, version, patterns, settings, mappings);
}
public static class Builder {
private static final Set<String> VALID_FIELDS = Sets.newHashSet(
"template", "order", "mappings", "settings", "index_patterns", "aliases", "version");
private String name;
private int order;
private Integer version;
private List<String> indexPatterns;
private Settings settings = Settings.Builder.EMPTY_SETTINGS;
private MappingMetaData mappings;
private final ImmutableOpenMap.Builder<String, AliasMetaData> aliases;
public Builder(String name) {
this.name = name;
mappings = null;
aliases = ImmutableOpenMap.builder();
}
public Builder(IndexTemplateMetaData indexTemplateMetaData) {
this.name = indexTemplateMetaData.name();
order(indexTemplateMetaData.order());
version(indexTemplateMetaData.version());
patterns(indexTemplateMetaData.patterns());
settings(indexTemplateMetaData.settings());
mappings = indexTemplateMetaData.mappings();
aliases = ImmutableOpenMap.builder(indexTemplateMetaData.aliases());
}
public Builder order(int order) {
this.order = order;
return this;
}
public Builder version(Integer version) {
this.version = version;
return this;
}
public Builder patterns(List<String> indexPatterns) {
this.indexPatterns = indexPatterns;
return this;
}
public Builder settings(Settings.Builder settings) {
this.settings = settings.build();
return this;
}
public Builder settings(Settings settings) {
this.settings = settings;
return this;
}
public Builder mapping(MappingMetaData mappings) {
this.mappings = mappings;
return this;
}
public Builder putAlias(AliasMetaData aliasMetaData) {
aliases.put(aliasMetaData.alias(), aliasMetaData);
return this;
}
public Builder putAlias(AliasMetaData.Builder aliasMetaData) {
aliases.put(aliasMetaData.alias(), aliasMetaData.build());
return this;
}
public IndexTemplateMetaData build() {
return new IndexTemplateMetaData(name, order, version, indexPatterns, settings, mappings, aliases.build());
}
public static IndexTemplateMetaData fromXContent(XContentParser parser, String templateName) throws IOException {
Builder builder = new Builder(templateName);
String currentFieldName = skipTemplateName(parser);
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("settings".equals(currentFieldName)) {
Settings.Builder templateSettingsBuilder = Settings.builder();
templateSettingsBuilder.put(Settings.fromXContent(parser));
templateSettingsBuilder.normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);
builder.settings(templateSettingsBuilder.build());
} else if ("mappings".equals(currentFieldName)) {
Map<String, Object> mapping = parser.map();
if (mapping.isEmpty() == false) {
MappingMetaData md = new MappingMetaData(MapperService.SINGLE_MAPPING_NAME, mapping);
builder.mapping(md);
}
} else if ("aliases".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
builder.putAlias(AliasMetaData.Builder.fromXContent(parser));
}
} else {
throw new ElasticsearchParseException("unknown key [{}] for index template", currentFieldName);
}
} else if (token == XContentParser.Token.START_ARRAY) {
if ("mappings".equals(currentFieldName)) {
// The server-side IndexTemplateMetaData has toXContent impl that can return mappings
// in an array but also a comment saying this never happens with typeless APIs.
throw new ElasticsearchParseException("Invalid response format - "
+ "mappings are not expected to be returned in an array", currentFieldName);
} else if ("index_patterns".equals(currentFieldName)) {
List<String> index_patterns = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
index_patterns.add(parser.text());
}
builder.patterns(index_patterns);
}
} else if (token.isValue()) {
// Prior to 5.1.0, elasticsearch only supported a single index pattern called `template` (#21009)
if("template".equals(currentFieldName)) {
builder.patterns(Collections.singletonList(parser.text()));
} else if ("order".equals(currentFieldName)) {
builder.order(parser.intValue());
} else if ("version".equals(currentFieldName)) {
builder.version(parser.intValue());
}
}
}
return builder.build();
}
private static String skipTemplateName(XContentParser parser) throws IOException {
XContentParser.Token token = parser.nextToken();
if (token == XContentParser.Token.START_OBJECT) {
token = parser.nextToken();
if (token == XContentParser.Token.FIELD_NAME) {
String currentFieldName = parser.currentName();
if (VALID_FIELDS.contains(currentFieldName)) {
return currentFieldName;
} else {
// we just hit the template name, which should be ignored and we move on
parser.nextToken();
}
}
}
return null;
}
}
}

View File

@ -0,0 +1,453 @@
/*
* 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.ElasticsearchGenerationException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.DeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static org.elasticsearch.action.ValidateActions.addValidationError;
import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
/**
* A request to create an index template.
*/
public class PutIndexTemplateRequest extends MasterNodeRequest<PutIndexTemplateRequest> implements IndicesRequest, ToXContent {
private String name;
private String cause = "";
private List<String> indexPatterns;
private int order;
private boolean create;
private Settings settings = EMPTY_SETTINGS;
private BytesReference mappings = null;
private final Set<Alias> aliases = new HashSet<>();
private Integer version;
/**
* Constructs a new put index template request with the provided name.
*/
public PutIndexTemplateRequest(String name) {
this.name(name);
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (indexPatterns == null || indexPatterns.size() == 0) {
validationException = addValidationError("index patterns are missing", validationException);
}
return validationException;
}
/**
* Sets the name of the index template.
*/
public PutIndexTemplateRequest name(String name) {
if(name == null) {
throw new IllegalArgumentException("Name cannot be null");
}
this.name = name;
return this;
}
/**
* The name of the index template.
*/
public String name() {
return this.name;
}
public PutIndexTemplateRequest patterns(List<String> indexPatterns) {
this.indexPatterns = indexPatterns;
return this;
}
public List<String> patterns() {
return this.indexPatterns;
}
public PutIndexTemplateRequest order(int order) {
this.order = order;
return this;
}
public int order() {
return this.order;
}
public PutIndexTemplateRequest version(Integer version) {
this.version = version;
return this;
}
public Integer version() {
return this.version;
}
/**
* 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 PutIndexTemplateRequest create(boolean create) {
this.create = create;
return this;
}
public boolean create() {
return create;
}
/**
* The settings to create the index template with.
*/
public PutIndexTemplateRequest settings(Settings settings) {
this.settings = settings;
return this;
}
/**
* The settings to create the index template with.
*/
public PutIndexTemplateRequest settings(Settings.Builder settings) {
this.settings = settings.build();
return this;
}
/**
* The settings to create the index template with (either json/yaml format).
*/
public PutIndexTemplateRequest settings(String source, XContentType xContentType) {
this.settings = Settings.builder().loadFromSource(source, xContentType).build();
return this;
}
/**
* The settings to create the index template with (either json or yaml format).
*/
public PutIndexTemplateRequest settings(Map<String, Object> source) {
try {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
builder.map(source);
settings(Strings.toString(builder), XContentType.JSON);
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to generate [" + source + "]", e);
}
return this;
}
public Settings settings() {
return this.settings;
}
/**
* Adds mapping that will be added when the index gets created.
*
* @param source The mapping source
* @param xContentType The type of content contained within the source
*/
public PutIndexTemplateRequest mapping(String source, XContentType xContentType) {
internalMapping(XContentHelper.convertToMap(new BytesArray(source), true, xContentType).v2());
return this;
}
/**
* The cause for this index template creation.
*/
public PutIndexTemplateRequest cause(String cause) {
this.cause = cause;
return this;
}
public String cause() {
return this.cause;
}
/**
* Adds mapping that will be added when the index gets created.
*
* @param source The mapping source
*/
public PutIndexTemplateRequest mapping(XContentBuilder source) {
internalMapping(XContentHelper.convertToMap(BytesReference.bytes(source),
true, source.contentType()).v2());
return this;
}
/**
* Adds mapping that will be added when the index gets created.
*
* @param source The mapping source
* @param xContentType the source content type
*/
public PutIndexTemplateRequest mapping(BytesReference source, XContentType xContentType) {
internalMapping(XContentHelper.convertToMap(source, true, xContentType).v2());
return this;
}
/**
* Adds mapping that will be added when the index gets created.
*
* @param source The mapping source
*/
public PutIndexTemplateRequest mapping(Map<String, Object> source) {
return internalMapping(source);
}
private PutIndexTemplateRequest internalMapping(Map<String, Object> source) {
try {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
builder.map(source);
Objects.requireNonNull(builder.contentType());
try {
mappings = new BytesArray(
XContentHelper.convertToJson(BytesReference.bytes(builder), false, false, builder.contentType()));
return this;
} catch (IOException e) {
throw new UncheckedIOException("failed to convert source to json", e);
}
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to generate [" + source + "]", e);
}
}
public BytesReference mappings() {
return this.mappings;
}
/**
* The template source definition.
*/
public PutIndexTemplateRequest source(XContentBuilder templateBuilder) {
try {
return source(BytesReference.bytes(templateBuilder), templateBuilder.contentType());
} catch (Exception e) {
throw new IllegalArgumentException("Failed to build json for template request", e);
}
}
/**
* The template source definition.
*/
@SuppressWarnings("unchecked")
public PutIndexTemplateRequest source(Map<String, Object> templateSource) {
Map<String, Object> source = templateSource;
for (Map.Entry<String, Object> entry : source.entrySet()) {
String name = entry.getKey();
if (name.equals("template")) {
if(entry.getValue() instanceof String) {
patterns(Collections.singletonList((String) entry.getValue()));
}
} else if (name.equals("index_patterns")) {
if(entry.getValue() instanceof String) {
patterns(Collections.singletonList((String) entry.getValue()));
} else if (entry.getValue() instanceof List) {
List<String> elements = ((List<?>) entry.getValue()).stream().map(Object::toString).collect(Collectors.toList());
patterns(elements);
} else {
throw new IllegalArgumentException("Malformed [template] value, should be a string or a list of strings");
}
} else if (name.equals("order")) {
order(XContentMapValues.nodeIntegerValue(entry.getValue(), order()));
} else if ("version".equals(name)) {
if ((entry.getValue() instanceof Integer) == false) {
throw new IllegalArgumentException("Malformed [version] value, should be an integer");
}
version((Integer)entry.getValue());
} else if (name.equals("settings")) {
if ((entry.getValue() instanceof Map) == false) {
throw new IllegalArgumentException("Malformed [settings] section, should include an inner object");
}
settings((Map<String, Object>) entry.getValue());
} else if (name.equals("mappings")) {
Map<String, Object> mappings = (Map<String, Object>) entry.getValue();
mapping(mappings);
} else if (name.equals("aliases")) {
aliases((Map<String, Object>) entry.getValue());
} else {
throw new ElasticsearchParseException("unknown key [{}] in the template ", name);
}
}
return this;
}
/**
* The template source definition.
*/
public PutIndexTemplateRequest source(String templateSource, XContentType xContentType) {
return source(XContentHelper.convertToMap(xContentType.xContent(), templateSource, true));
}
/**
* The template source definition.
*/
public PutIndexTemplateRequest source(byte[] source, XContentType xContentType) {
return source(source, 0, source.length, xContentType);
}
/**
* The template source definition.
*/
public PutIndexTemplateRequest source(byte[] source, int offset, int length, XContentType xContentType) {
return source(new BytesArray(source, offset, length), xContentType);
}
/**
* The template source definition.
*/
public PutIndexTemplateRequest source(BytesReference source, XContentType xContentType) {
return source(XContentHelper.convertToMap(source, true, xContentType).v2());
}
public Set<Alias> aliases() {
return this.aliases;
}
/**
* Sets the aliases that will be associated with the index when it gets created
*/
public PutIndexTemplateRequest aliases(Map<String, ?> source) {
try {
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.map(source);
return aliases(BytesReference.bytes(builder));
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to generate [" + source + "]", e);
}
}
/**
* Sets the aliases that will be associated with the index when it gets created
*/
public PutIndexTemplateRequest aliases(XContentBuilder source) {
return aliases(BytesReference.bytes(source));
}
/**
* Sets the aliases that will be associated with the index when it gets created
*/
public PutIndexTemplateRequest aliases(String source) {
return aliases(new BytesArray(source));
}
/**
* Sets the aliases that will be associated with the index when it gets created
*/
public PutIndexTemplateRequest aliases(BytesReference source) {
// EMPTY is safe here because we never call namedObject
try (XContentParser parser = XContentHelper
.createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, source)) {
//move to the first alias
parser.nextToken();
while ((parser.nextToken()) != XContentParser.Token.END_OBJECT) {
alias(Alias.fromXContent(parser));
}
return this;
} catch(IOException e) {
throw new ElasticsearchParseException("Failed to parse aliases", e);
}
}
/**
* Adds an alias that will be added when the index gets created.
*
* @param alias The metadata for the new alias
* @return the index template creation request
*/
public PutIndexTemplateRequest alias(Alias alias) {
aliases.add(alias);
return this;
}
@Override
public String[] indices() {
return indexPatterns.toArray(new String[indexPatterns.size()]);
}
@Override
public IndicesOptions indicesOptions() {
return IndicesOptions.strictExpand();
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
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();
if (mappings != null) {
builder.field("mappings");
try (XContentParser parser = JsonXContent.jsonXContent.createParser(NamedXContentRegistry.EMPTY,
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, mappings.utf8ToString())) {
builder.copyCurrentStructure(parser);
}
}
builder.startObject("aliases");
for (Alias alias : aliases) {
alias.toXContent(builder, params);
}
builder.endObject();
return builder;
}
}

View File

@ -54,8 +54,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.GetIndexTemplatesResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
import org.elasticsearch.action.index.IndexRequest;
@ -72,12 +70,14 @@ import org.elasticsearch.client.indices.GetFieldMappingsResponse;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.IndexTemplateMetaData;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.client.indices.UnfreezeIndexRequest;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.common.ValidationException;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Setting;
@ -86,6 +86,7 @@ import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.IndexSettings;
@ -97,6 +98,8 @@ import org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction;
import org.elasticsearch.rest.action.admin.indices.RestGetFieldMappingAction;
import org.elasticsearch.rest.action.admin.indices.RestGetMappingAction;
import org.elasticsearch.rest.action.admin.indices.RestPutMappingAction;
import org.elasticsearch.rest.action.admin.indices.RestGetIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestPutIndexTemplateAction;
import java.io.IOException;
import java.util.Arrays;
@ -1400,8 +1403,9 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
}
@SuppressWarnings("unchecked")
public void testPutTemplate() throws Exception {
PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest()
public void testPutTemplateWithTypes() throws Exception {
org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putTemplateRequest =
new org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest()
.name("my-template")
.patterns(Arrays.asList("pattern-1", "name-*"))
.order(10)
@ -1411,7 +1415,9 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
.alias(new Alias("alias-1").indexRouting("abc")).alias(new Alias("{index}-write").searchRouting("xyz"));
AcknowledgedResponse putTemplateResponse = execute(putTemplateRequest,
highLevelClient().indices()::putTemplate, highLevelClient().indices()::putTemplateAsync);
highLevelClient().indices()::putTemplate, highLevelClient().indices()::putTemplateAsync,
expectWarnings(RestPutIndexTemplateAction.TYPES_DEPRECATION_MESSAGE)
);
assertThat(putTemplateResponse.isAcknowledged(), equalTo(true));
Map<String, Object> templates = getAsMap("/_template/my-template");
@ -1425,6 +1431,94 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
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"));
}
@SuppressWarnings("unchecked")
public void testPutTemplate() throws Exception {
PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest("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("{ \"properties\":{"
+ "\"host_name\": {\"type\":\"keyword\"}"
+ "}"
+ "}", XContentType.JSON)
.alias(new Alias("alias-1").indexRouting("abc")).alias(new Alias("{index}-write").searchRouting("xyz"));
AcknowledgedResponse 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.properties.host_name.type", templates), equalTo("keyword"));
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 testPutTemplateWithTypesUsingUntypedAPI() throws Exception {
PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest("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("{ "
+ "\"my_doc_type\":{"
+ "\"properties\":{"
+ "\"host_name\": {\"type\":\"keyword\"}"
+ "}"
+ "}"
+ "}", XContentType.JSON)
.alias(new Alias("alias-1").indexRouting("abc")).alias(new Alias("{index}-write").searchRouting("xyz"));
ElasticsearchStatusException badMappingError = expectThrows(ElasticsearchStatusException.class,
() -> execute(putTemplateRequest,
highLevelClient().indices()::putTemplate, highLevelClient().indices()::putTemplateAsync));
assertThat(badMappingError.getDetailedMessage(),
containsString("Root mapping definition has unsupported parameters: [my_doc_type"));
}
@SuppressWarnings("unchecked")
public void testPutTemplateWithNoTypesUsingTypedApi() throws Exception {
org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putTemplateRequest =
new org.elasticsearch.action.admin.indices.template.put.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("my_doc_type",
// Note that the declared type is missing from the mapping
"{ "
+ "\"properties\":{"
+ "\"host_name\": {\"type\":\"keyword\"},"
+ "\"description\": {\"type\":\"text\"}"
+ "}"
+ "}", XContentType.JSON)
.alias(new Alias("alias-1").indexRouting("abc")).alias(new Alias("{index}-write").searchRouting("xyz"));
AcknowledgedResponse putTemplateResponse = execute(putTemplateRequest,
highLevelClient().indices()::putTemplate, highLevelClient().indices()::putTemplateAsync,
expectWarnings(RestPutIndexTemplateAction.TYPES_DEPRECATION_MESSAGE)
);
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.properties.host_name.type", templates), equalTo("keyword"));
assertThat(extractValue("my-template.mappings.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();
@ -1489,50 +1583,68 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertFalse(response.isValid());
}
public void testCRUDIndexTemplate() throws Exception {
// Tests the deprecated form of the API that returns templates with doc types (using the server-side's GetIndexTemplateResponse)
public void testCRUDIndexTemplateWithTypes() throws Exception {
RestHighLevelClient client = highLevelClient();
PutIndexTemplateRequest putTemplate1 = new PutIndexTemplateRequest().name("template-1")
org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putTemplate1 =
new org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest().name("template-1")
.patterns(Arrays.asList("pattern-1", "name-1")).alias(new Alias("alias-1"));
assertThat(execute(putTemplate1, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(),
equalTo(true));
PutIndexTemplateRequest putTemplate2 = new PutIndexTemplateRequest().name("template-2")
assertThat(execute(putTemplate1, client.indices()::putTemplate, client.indices()::putTemplateAsync
, expectWarnings(RestPutIndexTemplateAction.TYPES_DEPRECATION_MESSAGE))
.isAcknowledged(), equalTo(true));
org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putTemplate2 =
new org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest().name("template-2")
.patterns(Arrays.asList("pattern-2", "name-2"))
.mapping("custom_doc_type", "name", "type=text")
.settings(Settings.builder().put("number_of_shards", "2").put("number_of_replicas", "0"));
assertThat(execute(putTemplate2, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(),
equalTo(true));
assertThat(execute(putTemplate2, client.indices()::putTemplate, client.indices()::putTemplateAsync,
expectWarnings(RestPutIndexTemplateAction.TYPES_DEPRECATION_MESSAGE))
.isAcknowledged(), equalTo(true));
GetIndexTemplatesResponse getTemplate1 = execute(new GetIndexTemplatesRequest("template-1"),
client.indices()::getTemplate, client.indices()::getTemplateAsync);
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse getTemplate1 = execute(
new GetIndexTemplatesRequest("template-1"),
client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE));
assertThat(getTemplate1.getIndexTemplates(), hasSize(1));
IndexTemplateMetaData template1 = getTemplate1.getIndexTemplates().get(0);
org.elasticsearch.cluster.metadata.IndexTemplateMetaData template1 = getTemplate1.getIndexTemplates().get(0);
assertThat(template1.name(), equalTo("template-1"));
assertThat(template1.patterns(), contains("pattern-1", "name-1"));
assertTrue(template1.aliases().containsKey("alias-1"));
GetIndexTemplatesResponse getTemplate2 = execute(new GetIndexTemplatesRequest("template-2"),
client.indices()::getTemplate, client.indices()::getTemplateAsync);
//Check the typed version of the call
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse getTemplate2 =
execute(new GetIndexTemplatesRequest("template-2"),
client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE));
assertThat(getTemplate2.getIndexTemplates(), hasSize(1));
IndexTemplateMetaData template2 = getTemplate2.getIndexTemplates().get(0);
org.elasticsearch.cluster.metadata.IndexTemplateMetaData template2 = getTemplate2.getIndexTemplates().get(0);
assertThat(template2.name(), equalTo("template-2"));
assertThat(template2.patterns(), contains("pattern-2", "name-2"));
assertTrue(template2.aliases().isEmpty());
assertThat(template2.settings().get("index.number_of_shards"), equalTo("2"));
assertThat(template2.settings().get("index.number_of_replicas"), equalTo("0"));
assertThat(template2.settings().get("index.number_of_replicas"), equalTo("0"));
// Ugly deprecated form of API requires use of doc type to get at mapping object which is CompressedXContent
assertTrue(template2.mappings().containsKey("custom_doc_type"));
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);
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse getBoth = execute(
getBothRequest, client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE));
assertThat(getBoth.getIndexTemplates(), hasSize(2));
assertThat(getBoth.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).toArray(),
assertThat(getBoth.getIndexTemplates().stream().map(org.elasticsearch.cluster.metadata.IndexTemplateMetaData::getName).toArray(),
arrayContainingInAnyOrder("template-1", "template-2"));
GetIndexTemplatesRequest getAllRequest = new GetIndexTemplatesRequest();
GetIndexTemplatesResponse getAll = execute(getAllRequest, client.indices()::getTemplate, client.indices()::getTemplateAsync);
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse getAll = execute(
getAllRequest, client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE));
assertThat(getAll.getIndexTemplates().size(), greaterThanOrEqualTo(2));
assertThat(getAll.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).collect(Collectors.toList()),
assertThat(getAll.getIndexTemplates().stream().map(org.elasticsearch.cluster.metadata.IndexTemplateMetaData::getName)
.collect(Collectors.toList()),
hasItems("template-1", "template-2"));
assertTrue(execute(new DeleteIndexTemplateRequest("template-1"),
@ -1543,14 +1655,96 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
client.indices()::deleteTemplate, client.indices()::deleteTemplateAsync)).status(), equalTo(RestStatus.NOT_FOUND));
assertThat(execute(new GetIndexTemplatesRequest("template-*"),
client.indices()::getTemplate, client.indices()::getTemplateAsync).getIndexTemplates(), hasSize(1));
client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE)).getIndexTemplates(), hasSize(1));
assertThat(execute(new GetIndexTemplatesRequest("template-*"),
client.indices()::getTemplate, client.indices()::getTemplateAsync).getIndexTemplates().get(0).name(), equalTo("template-2"));
client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE)).getIndexTemplates()
.get(0).name(), equalTo("template-2"));
assertTrue(execute(new DeleteIndexTemplateRequest("template-*"),
client.indices()::deleteTemplate, client.indices()::deleteTemplateAsync).isAcknowledged());
assertThat(expectThrows(ElasticsearchException.class, () -> execute(new GetIndexTemplatesRequest("template-*"),
client.indices()::getTemplate, client.indices()::getTemplateAsync)).status(), equalTo(RestStatus.NOT_FOUND));
client.indices()::getTemplate, client.indices()::getTemplateAsync,
expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE))).status(), equalTo(RestStatus.NOT_FOUND));
}
public void testCRUDIndexTemplate() throws Exception {
RestHighLevelClient client = highLevelClient();
PutIndexTemplateRequest putTemplate1 = new PutIndexTemplateRequest("template-1")
.patterns(Arrays.asList("pattern-1", "name-1")).alias(new Alias("alias-1"));
assertThat(execute(putTemplate1, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(),
equalTo(true));
PutIndexTemplateRequest putTemplate2 = new PutIndexTemplateRequest("template-2")
.patterns(Arrays.asList("pattern-2", "name-2"))
.mapping("{\"properties\": { \"name\": { \"type\": \"text\" }}}", XContentType.JSON)
.settings(Settings.builder().put("number_of_shards", "2").put("number_of_replicas", "0"));
assertThat(execute(putTemplate2, client.indices()::putTemplate, client.indices()::putTemplateAsync)
.isAcknowledged(), equalTo(true));
GetIndexTemplatesResponse getTemplate1 = execute(
new GetIndexTemplatesRequest("template-1"),
client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync);
assertThat(getTemplate1.getIndexTemplates(), hasSize(1));
IndexTemplateMetaData template1 = getTemplate1.getIndexTemplates().get(0);
assertThat(template1.name(), equalTo("template-1"));
assertThat(template1.patterns(), contains("pattern-1", "name-1"));
assertTrue(template1.aliases().containsKey("alias-1"));
GetIndexTemplatesResponse getTemplate2 = execute(new GetIndexTemplatesRequest("template-2"),
client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync);
assertThat(getTemplate2.getIndexTemplates(), hasSize(1));
IndexTemplateMetaData template2 = getTemplate2.getIndexTemplates().get(0);
assertThat(template2.name(), equalTo("template-2"));
assertThat(template2.patterns(), contains("pattern-2", "name-2"));
assertTrue(template2.aliases().isEmpty());
assertThat(template2.settings().get("index.number_of_shards"), equalTo("2"));
assertThat(template2.settings().get("index.number_of_replicas"), equalTo("0"));
// New API returns a MappingMetaData class rather than CompressedXContent for the mapping
assertTrue(template2.mappings().sourceAsMap().containsKey("properties"));
@SuppressWarnings("unchecked")
Map<String, Object> props = (Map<String, Object>) template2.mappings().sourceAsMap().get("properties");
assertTrue(props.containsKey("name"));
List<String> names = randomBoolean()
? Arrays.asList("*-1", "template-2")
: Arrays.asList("template-*");
GetIndexTemplatesRequest getBothRequest = new GetIndexTemplatesRequest(names);
GetIndexTemplatesResponse getBoth = execute(
getBothRequest, client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync);
assertThat(getBoth.getIndexTemplates(), hasSize(2));
assertThat(getBoth.getIndexTemplates().stream().map(IndexTemplateMetaData::name).toArray(),
arrayContainingInAnyOrder("template-1", "template-2"));
GetIndexTemplatesRequest getAllRequest = new GetIndexTemplatesRequest();
GetIndexTemplatesResponse getAll = execute(
getAllRequest, client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync);
assertThat(getAll.getIndexTemplates().size(), greaterThanOrEqualTo(2));
assertThat(getAll.getIndexTemplates().stream().map(IndexTemplateMetaData::name)
.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("template-1"),
client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync)).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));
assertThat(execute(new GetIndexTemplatesRequest("template-*"),
client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync).getIndexTemplates(), hasSize(1));
assertThat(execute(new GetIndexTemplatesRequest("template-*"),
client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync).getIndexTemplates()
.get(0).name(), equalTo("template-2"));
assertTrue(execute(new DeleteIndexTemplateRequest("template-*"),
client.indices()::deleteTemplate, client.indices()::deleteTemplateAsync).isAcknowledged());
assertThat(expectThrows(ElasticsearchException.class, () -> execute(new GetIndexTemplatesRequest("template-*"),
client.indices()::getIndexTemplate, client.indices()::getIndexTemplateAsync)).status(), equalTo(RestStatus.NOT_FOUND));
}
public void testIndexTemplatesExist() throws Exception {
@ -1559,8 +1753,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
{
for (String suffix : Arrays.asList("1", "2")) {
final PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest()
.name("template-" + suffix)
final PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest("template-" + suffix)
.patterns(Arrays.asList("pattern-" + suffix, "name-" + suffix))
.alias(new Alias("alias-" + suffix));
assertTrue(execute(putRequest, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged());

View File

@ -45,7 +45,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.ResizeType;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
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.CreateIndexRequest;
@ -53,6 +52,7 @@ import org.elasticsearch.client.indices.GetFieldMappingsRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.client.indices.RandomCreateIndexGenerator;
import org.elasticsearch.common.CheckedFunction;
@ -60,6 +60,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import org.junit.Assert;
@ -921,14 +922,16 @@ public class IndicesRequestConvertersTests extends ESTestCase {
Assert.assertEquals(expectedParams, request.getParameters());
}
public void testPutTemplateRequest() throws Exception {
public void testPutTemplateRequestWithTypes() 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(ESTestCase.randomFrom(names.keySet()))
org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putTemplateRequest =
new org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest()
.name(ESTestCase.randomFrom(names.keySet()))
.patterns(Arrays.asList(ESTestCase.generateRandomStringArray(20, 100, false, false)));
if (ESTestCase.randomBoolean()) {
putTemplateRequest.order(ESTestCase.randomInt());
@ -939,14 +942,15 @@ public class IndicesRequestConvertersTests extends ESTestCase {
if (ESTestCase.randomBoolean()) {
putTemplateRequest.settings(Settings.builder().put("setting-" + ESTestCase.randomInt(), ESTestCase.randomTimeValue()));
}
Map<String, String> expectedParams = new HashMap<>();
if (ESTestCase.randomBoolean()) {
putTemplateRequest.mapping("doc-" + ESTestCase.randomInt(),
"field-" + ESTestCase.randomInt(), "type=" + ESTestCase.randomFrom("text", "keyword"));
}
expectedParams.put(INCLUDE_TYPE_NAME_PARAMETER, "true");
if (ESTestCase.randomBoolean()) {
putTemplateRequest.alias(new Alias("alias-" + ESTestCase.randomInt()));
}
Map<String, String> expectedParams = new HashMap<>();
if (ESTestCase.randomBoolean()) {
expectedParams.put("create", Boolean.TRUE.toString());
putTemplateRequest.create(true);
@ -955,9 +959,8 @@ public class IndicesRequestConvertersTests extends ESTestCase {
String cause = ESTestCase.randomUnicodeOfCodepointLengthBetween(1, 50);
putTemplateRequest.cause(cause);
expectedParams.put("cause", cause);
}
}
RequestConvertersTests.setRandomMasterTimeout(putTemplateRequest, expectedParams);
expectedParams.put(INCLUDE_TYPE_NAME_PARAMETER, "true");
Request request = IndicesRequestConverters.putTemplate(putTemplateRequest);
Assert.assertThat(request.getEndpoint(), equalTo("/_template/" + names.get(putTemplateRequest.name())));
@ -965,6 +968,49 @@ public class IndicesRequestConvertersTests extends ESTestCase {
RequestConvertersTests.assertToXContentBody(putTemplateRequest, request.getEntity());
}
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(ESTestCase.randomFrom(names.keySet()))
.patterns(Arrays.asList(ESTestCase.generateRandomStringArray(20, 100, false, false)));
if (ESTestCase.randomBoolean()) {
putTemplateRequest.order(ESTestCase.randomInt());
}
if (ESTestCase.randomBoolean()) {
putTemplateRequest.version(ESTestCase.randomInt());
}
if (ESTestCase.randomBoolean()) {
putTemplateRequest.settings(Settings.builder().put("setting-" + ESTestCase.randomInt(), ESTestCase.randomTimeValue()));
}
Map<String, String> expectedParams = new HashMap<>();
if (ESTestCase.randomBoolean()) {
putTemplateRequest.mapping("{ \"properties\": { \"field-" + ESTestCase.randomInt() +
"\" : { \"type\" : \"" + ESTestCase.randomFrom("text", "keyword") + "\" }}}", XContentType.JSON);
}
if (ESTestCase.randomBoolean()) {
putTemplateRequest.alias(new Alias("alias-" + ESTestCase.randomInt()));
}
if (ESTestCase.randomBoolean()) {
expectedParams.put("create", Boolean.TRUE.toString());
putTemplateRequest.create(true);
}
if (ESTestCase.randomBoolean()) {
String cause = ESTestCase.randomUnicodeOfCodepointLengthBetween(1, 50);
putTemplateRequest.cause(cause);
expectedParams.put("cause", cause);
}
RequestConvertersTests.setRandomMasterTimeout(putTemplateRequest, expectedParams);
Request request = IndicesRequestConverters.putTemplate(putTemplateRequest);
Assert.assertThat(request.getEndpoint(), equalTo("/_template/" + names.get(putTemplateRequest.name())));
Assert.assertThat(request.getParameters(), equalTo(expectedParams));
RequestConvertersTests.assertToXContentBody(putTemplateRequest, request.getEntity());
}
public void testValidateQuery() throws Exception {
String[] indices = ESTestCase.randomBoolean() ? null : RequestConvertersTests.randomIndicesNames(0, 5);
String[] types = ESTestCase.randomBoolean() ? ESTestCase.generateRandomStringArray(5, 5, false, false) : null;
@ -1012,9 +1058,9 @@ public class IndicesRequestConvertersTests extends ESTestCase {
Map<String, String> expectedParams = new HashMap<>();
RequestConvertersTests.setRandomMasterTimeout(getTemplatesRequest::setMasterNodeTimeout, expectedParams);
RequestConvertersTests.setRandomLocal(getTemplatesRequest::setLocal, expectedParams);
expectedParams.put(INCLUDE_TYPE_NAME_PARAMETER, "true");
Request request = IndicesRequestConverters.getTemplates(getTemplatesRequest);
Request request = IndicesRequestConverters.getTemplatesWithDocumentTypes(getTemplatesRequest);
expectedParams.put(INCLUDE_TYPE_NAME_PARAMETER, "true");
Assert.assertThat(request.getEndpoint(),
equalTo("/_template/" + names.stream().map(encodes::get).collect(Collectors.joining(","))));
Assert.assertThat(request.getParameters(), equalTo(expectedParams));

View File

@ -744,6 +744,14 @@ public class RestHighLevelClientTests extends ESTestCase {
.collect(Collectors.groupingBy(Tuple::v1,
Collectors.mapping(Tuple::v2, Collectors.toSet())));
// TODO remove in 8.0 - we will undeprecate indices.get_template because the current getIndexTemplate
// impl will replace the existing getTemplate method.
// The above general-purpose code ignores all deprecated methods which in this case leaves `getTemplate`
// looking like it doesn't have a valid implementatation when it does.
apiUnsupported.remove("indices.get_template");
for (Map.Entry<String, Set<Method>> entry : methods.entrySet()) {
String apiName = entry.getKey();
@ -776,7 +784,10 @@ public class RestHighLevelClientTests extends ESTestCase {
apiName.startsWith("security.") == false &&
apiName.startsWith("index_lifecycle.") == false &&
apiName.startsWith("ccr.") == false &&
apiName.endsWith("freeze") == false) {
apiName.endsWith("freeze") == false &&
// IndicesClientIT.getIndexTemplate should be renamed "getTemplate" in version 8.0 when we
// can get rid of 7.0's deprecated "getTemplate"
apiName.equals("indices.get_index_template") == false) {
apiNotFound.add(apiName);
}
}

View File

@ -55,8 +55,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.GetIndexTemplatesResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.validate.query.QueryExplanation;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryResponse;
@ -76,11 +74,13 @@ import org.elasticsearch.client.indices.FreezeIndexRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.IndexTemplateMetaData;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.client.indices.UnfreezeIndexRequest;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
@ -2094,13 +2094,11 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
{
// tag::put-template-request-mappings-json
request.mapping("_doc", // <1>
request.mapping(// <1>
"{\n" +
" \"_doc\": {\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" }\n" +
"}", // <2>
@ -2111,14 +2109,16 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
{
//tag::put-template-request-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> mapping = new HashMap<>();
mapping.put("properties", properties);
jsonMap.put("_doc", mapping);
request.mapping("_doc", jsonMap); // <1>
{
Map<String, Object> properties = new HashMap<>();
{
Map<String, Object> message = new HashMap<>();
message.put("type", "text");
properties.put("message", message);
}
jsonMap.put("properties", properties);
}
request.mapping(jsonMap); // <1>
//end::put-template-request-mappings-map
assertTrue(client.indices().putTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
}
@ -2127,31 +2127,21 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.startObject("_doc");
builder.startObject("properties");
{
builder.startObject("properties");
builder.startObject("message");
{
builder.startObject("message");
{
builder.field("type", "text");
}
builder.endObject();
builder.field("type", "text");
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
request.mapping("_doc", builder); // <1>
request.mapping(builder); // <1>
//end::put-template-request-mappings-xcontent
assertTrue(client.indices().putTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
}
{
//tag::put-template-request-mappings-shortcut
request.mapping("_doc", "message", "type=text"); // <1>
//end::put-template-request-mappings-shortcut
assertTrue(client.indices().putTemplate(request, RequestOptions.DEFAULT).isAcknowledged());
}
// tag::put-template-request-aliases
request.alias(new Alias("twitter_alias").filter(QueryBuilders.termQuery("user", "kimchy"))); // <1>
@ -2177,11 +2167,9 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
" \"number_of_shards\": 1\n" +
" },\n" +
" \"mappings\": {\n" +
" \"_doc\": {\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" }\n" +
" },\n" +
@ -2244,13 +2232,11 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest("my-template");
putRequest.patterns(Arrays.asList("pattern-1", "log-*"));
putRequest.settings(Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 1));
putRequest.mapping("_doc",
putRequest.mapping(
"{\n" +
" \"_doc\": {\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" \"properties\": {\n" +
" \"message\": {\n" +
" \"type\": \"text\"\n" +
" }\n" +
" }\n" +
"}", XContentType.JSON);
@ -2269,7 +2255,7 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
// end::get-templates-request-masterTimeout
// tag::get-templates-execute
GetIndexTemplatesResponse getTemplatesResponse = client.indices().getTemplate(request, RequestOptions.DEFAULT);
GetIndexTemplatesResponse getTemplatesResponse = client.indices().getIndexTemplate(request, RequestOptions.DEFAULT);
// end::get-templates-execute
// tag::get-templates-response
@ -2299,7 +2285,7 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
listener = new LatchedActionListener<>(listener, latch);
// tag::get-templates-execute-async
client.indices().getTemplateAsync(request, RequestOptions.DEFAULT, listener); // <1>
client.indices().getIndexTemplateAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::get-templates-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));

View File

@ -0,0 +1,136 @@
/*
* 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.AliasMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester;
public class GetIndexTemplatesResponseTests extends ESTestCase {
static final String mappingString = "{\"properties\":{"
+ "\"f1\": {\"type\":\"text\"},"
+ "\"f2\": {\"type\":\"keyword\"}"
+ "}}";
public void testFromXContent() throws IOException {
xContentTester(this::createParser, GetIndexTemplatesResponseTests::createTestInstance, GetIndexTemplatesResponseTests::toXContent,
GetIndexTemplatesResponse::fromXContent).supportsUnknownFields(false)
.assertEqualsConsumer(GetIndexTemplatesResponseTests::assertEqualInstances)
.shuffleFieldsExceptions(new String[] {"aliases", "mappings", "patterns", "settings"})
.test();
}
private static void assertEqualInstances(GetIndexTemplatesResponse expectedInstance, GetIndexTemplatesResponse newInstance) {
assertEquals(expectedInstance, newInstance);
// Check there's no doc types at the root of the mapping
Map<String, Object> expectedMap = XContentHelper.convertToMap(
new BytesArray(mappingString), true, XContentType.JSON).v2();
for (IndexTemplateMetaData template : newInstance.getIndexTemplates()) {
MappingMetaData mappingMD = template.mappings();
if(mappingMD!=null) {
Map<String, Object> mappingAsMap = mappingMD.sourceAsMap();
assertEquals(expectedMap, mappingAsMap);
}
}
}
static GetIndexTemplatesResponse createTestInstance() {
List<IndexTemplateMetaData> templates = new ArrayList<>();
int numTemplates = between(0, 10);
for (int t = 0; t < numTemplates; t++) {
IndexTemplateMetaData.Builder templateBuilder = IndexTemplateMetaData.builder("template-" + t);
templateBuilder.patterns(IntStream.range(0, between(1, 5)).mapToObj(i -> "pattern-" + i).collect(Collectors.toList()));
int numAlias = between(0, 5);
for (int i = 0; i < numAlias; i++) {
templateBuilder.putAlias(AliasMetaData.builder(randomAlphaOfLengthBetween(1, 10)));
}
if (randomBoolean()) {
templateBuilder.settings(Settings.builder().put("index.setting-1", randomLong()));
}
if (randomBoolean()) {
templateBuilder.order(randomInt());
}
if (randomBoolean()) {
templateBuilder.version(between(0, 100));
}
if (randomBoolean()) {
try {
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(mappingString), true, XContentType.JSON).v2();
MappingMetaData mapping = new MappingMetaData(MapperService.SINGLE_MAPPING_NAME, map);
templateBuilder.mapping(mapping);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
templates.add(templateBuilder.build());
}
return new GetIndexTemplatesResponse(templates);
}
// As the client class GetIndexTemplatesResponse doesn't have toXContent method, adding this method here only for the test
static void toXContent(GetIndexTemplatesResponse response, XContentBuilder builder) throws IOException {
//Create a server-side counterpart for the client-side class and call toXContent on it
List<org.elasticsearch.cluster.metadata.IndexTemplateMetaData> serverIndexTemplates = new ArrayList<>();
List<IndexTemplateMetaData> clientIndexTemplates = response.getIndexTemplates();
for (IndexTemplateMetaData clientITMD : clientIndexTemplates) {
org.elasticsearch.cluster.metadata.IndexTemplateMetaData.Builder serverTemplateBuilder =
org.elasticsearch.cluster.metadata.IndexTemplateMetaData.builder(clientITMD.name());
serverTemplateBuilder.patterns(clientITMD.patterns());
Iterator<AliasMetaData> aliases = clientITMD.aliases().valuesIt();
aliases.forEachRemaining((a)->serverTemplateBuilder.putAlias(a));
serverTemplateBuilder.settings(clientITMD.settings());
serverTemplateBuilder.order(clientITMD.order());
serverTemplateBuilder.version(clientITMD.version());
if (clientITMD.mappings() != null) {
serverTemplateBuilder.putMapping(MapperService.SINGLE_MAPPING_NAME, clientITMD.mappings().source());
}
serverIndexTemplates.add(serverTemplateBuilder.build());
}
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse serverResponse = new
org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse(serverIndexTemplates);
serverResponse.toXContent(builder, ToXContent.EMPTY_PARAMS);
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.action.ActionRequestValidationException;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
public class PutIndexTemplateRequestTests extends AbstractXContentTestCase<PutIndexTemplateRequest> {
public void testValidateErrorMessage() throws Exception {
expectThrows(IllegalArgumentException.class, () -> new PutIndexTemplateRequest(null));
expectThrows(IllegalArgumentException.class, () -> new PutIndexTemplateRequest("test").name(null));
PutIndexTemplateRequest request = new PutIndexTemplateRequest("test");
ActionRequestValidationException withoutPattern = request.validate();
assertThat(withoutPattern.getMessage(), containsString("index patterns are missing"));
request.name("foo");
ActionRequestValidationException withoutIndexPatterns = request.validate();
assertThat(withoutIndexPatterns.validationErrors(), hasSize(1));
assertThat(withoutIndexPatterns.getMessage(), containsString("index patterns are missing"));
request.patterns(Collections.singletonList("test-*"));
ActionRequestValidationException noError = request.validate();
assertThat(noError, is(nullValue()));
}
@Override
protected PutIndexTemplateRequest createTestInstance() {
PutIndexTemplateRequest request = new PutIndexTemplateRequest("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++) {
// some ASCII or Latin-1 control characters, especially newline, can lead to
// problems with yaml parsers, that's why we filter them here (see #30911)
Alias alias = new Alias(randomRealisticUnicodeOfLengthBetween(1, 10).replaceAll("\\p{Cc}", ""));
if (randomBoolean()) {
alias.indexRouting(randomRealisticUnicodeOfLengthBetween(1, 10));
}
if (randomBoolean()) {
alias.searchRouting(randomRealisticUnicodeOfLengthBetween(1, 10));
}
request.alias(alias);
}
if (randomBoolean()) {
try {
request.mapping(XContentFactory.jsonBuilder().startObject()
.startObject("properties")
.startObject("field-" + randomInt()).field("type", randomFrom("keyword", "text")).endObject()
.endObject().endObject());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
if (randomBoolean()) {
request.settings(Settings.builder().put("setting1", randomLong()).put("setting2", randomTimeValue()).build());
}
return request;
}
@Override
protected PutIndexTemplateRequest doParseInstance(XContentParser parser) throws IOException {
return new PutIndexTemplateRequest("test").source(parser.map());
}
@Override
protected void assertEqualInstances(PutIndexTemplateRequest expected, PutIndexTemplateRequest actual) {
assertNotSame(expected, actual);
assertThat(actual.version(), equalTo(expected.version()));
assertThat(actual.order(), equalTo(expected.order()));
assertThat(actual.patterns(), equalTo(expected.patterns()));
assertThat(actual.aliases(), equalTo(expected.aliases()));
assertThat(actual.mappings(), equalTo(expected.mappings()));
assertThat(actual.settings(), equalTo(expected.settings()));
}
@Override
protected boolean supportsUnknownFields() {
return false;
}
}

View File

@ -39,8 +39,7 @@ template's patterns.
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-request-mappings-json]
--------------------------------------------------
<1> The type to define
<2> The mapping for this type, provided as a JSON string
<1> The mapping, provided as a JSON string
The mapping source can be provided in different ways in addition to the
`String` example shown above:
@ -59,13 +58,6 @@ include-tagged::{doc-tests-file}[{api}-request-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-file}[{api}-request-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.

View File

@ -35,6 +35,8 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.rest.action.admin.indices.RestGetIndexTemplateAction;
import org.elasticsearch.rest.action.admin.indices.RestPutIndexTemplateAction;
import org.elasticsearch.rest.action.document.RestBulkAction;
import org.elasticsearch.rest.action.document.RestGetAction;
import org.elasticsearch.rest.action.document.RestUpdateAction;
@ -921,6 +923,7 @@ public class FullClusterRestartIT extends AbstractFullClusterRestartTestCase {
// We therefore use the deprecated typed APIs when running against the current version.
if (isRunningAgainstOldCluster() == false) {
createTemplateRequest.addParameter(INCLUDE_TYPE_NAME_PARAMETER, "true");
createTemplateRequest.setOptions(expectWarnings(RestPutIndexTemplateAction.TYPES_DEPRECATION_MESSAGE));
}
client().performRequest(createTemplateRequest);
@ -1122,6 +1125,7 @@ public class FullClusterRestartIT extends AbstractFullClusterRestartTestCase {
// We therefore use the deprecated typed APIs when running against the current version.
if (isRunningAgainstOldCluster() == false) {
getTemplateRequest.addParameter(INCLUDE_TYPE_NAME_PARAMETER, "true");
getTemplateRequest.setOptions(expectWarnings(RestGetIndexTemplateAction.TYPES_DEPRECATION_MESSAGE));
}
Map<String, Object> getTemplateResponse = entityAsMap(client().performRequest(getTemplateRequest));

View File

@ -43,7 +43,7 @@ public class GetIndexTemplatesResponse extends ActionResponse implements ToXCont
indexTemplates = new ArrayList<>();
}
GetIndexTemplatesResponse(List<IndexTemplateMetaData> indexTemplates) {
public GetIndexTemplatesResponse(List<IndexTemplateMetaData> indexTemplates) {
this.indexTemplates = indexTemplates;
}

View File

@ -19,10 +19,12 @@
package org.elasticsearch.rest.action.admin.indices;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.rest.BaseRestHandler;
@ -47,6 +49,10 @@ public class RestGetIndexTemplateAction extends BaseRestHandler {
private static final Set<String> RESPONSE_PARAMETERS = Collections.unmodifiableSet(Sets.union(
Collections.singleton(INCLUDE_TYPE_NAME_PARAMETER), Settings.FORMAT_PARAMS));
private static final DeprecationLogger deprecationLogger = new DeprecationLogger(
LogManager.getLogger(RestGetIndexTemplateAction.class));
public static final String TYPES_DEPRECATION_MESSAGE = "[types removal]" +
" Specifying include_type_name in get index template requests is deprecated.";
public RestGetIndexTemplateAction(final Settings settings, final RestController controller) {
super(settings);
@ -65,6 +71,9 @@ public class RestGetIndexTemplateAction extends BaseRestHandler {
final String[] names = Strings.splitStringByCommaToArray(request.param("name"));
final GetIndexTemplatesRequest getIndexTemplatesRequest = new GetIndexTemplatesRequest(names);
if (request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
deprecationLogger.deprecatedAndMaybeLog("get_index_template_include_type_name", TYPES_DEPRECATION_MESSAGE);
}
getIndexTemplatesRequest.local(request.paramAsBoolean("local", getIndexTemplatesRequest.local()));
getIndexTemplatesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getIndexTemplatesRequest.masterNodeTimeout()));

View File

@ -42,6 +42,9 @@ public class RestPutIndexTemplateAction extends BaseRestHandler {
private static final DeprecationLogger deprecationLogger = new DeprecationLogger(
LogManager.getLogger(RestPutIndexTemplateAction.class));
public static final String TYPES_DEPRECATION_MESSAGE = "[types removal]" +
" Specifying include_type_name in put index template requests is deprecated."+
" The parameter will be removed in the next major version.";
public RestPutIndexTemplateAction(Settings settings, RestController controller) {
super(settings);
@ -57,6 +60,9 @@ public class RestPutIndexTemplateAction extends BaseRestHandler {
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest(request.param("name"));
if (request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
deprecationLogger.deprecatedAndMaybeLog("put_index_template_with_types", TYPES_DEPRECATION_MESSAGE);
}
if (request.hasParam("template")) {
deprecationLogger.deprecated("Deprecated parameter[template] used, replaced by [index_patterns]");
putRequest.patterns(Collections.singletonList(request.param("template")));

View File

@ -19,6 +19,7 @@
package org.elasticsearch.rest.action.admin.indices;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -31,8 +32,12 @@ import org.elasticsearch.test.rest.RestActionTestCase;
import org.junit.Before;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.rest.BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER;
import static org.mockito.Mockito.mock;
public class RestPutIndexTemplateActionTests extends RestActionTestCase {
private RestPutIndexTemplateAction action;
@ -45,7 +50,8 @@ public class RestPutIndexTemplateActionTests extends RestActionTestCase {
XContentBuilder content = XContentFactory.jsonBuilder().startObject()
.startObject("mappings")
.startObject("properties")
.startObject("field").field("type", "keyword").endObject()
.startObject("field1").field("type", "keyword").endObject()
.startObject("field2").field("type", "text").endObject()
.endObject()
.endObject()
.startObject("aliases")
@ -58,6 +64,11 @@ public class RestPutIndexTemplateActionTests extends RestActionTestCase {
.withPath("/_template/_some_template")
.withContent(BytesReference.bytes(content), XContentType.JSON)
.build();
action.prepareRequest(request, mock(NodeClient.class));
// Internally the above prepareRequest method calls prepareRequestSource to inject a
// default type into the mapping. Here we test that this does what is expected by
// explicitly calling that same helper function
boolean includeTypeName = false;
Map<String, Object> source = action.prepareRequestSource(request, includeTypeName);
@ -65,7 +76,8 @@ public class RestPutIndexTemplateActionTests extends RestActionTestCase {
.startObject("mappings")
.startObject("_doc")
.startObject("properties")
.startObject("field").field("type", "keyword").endObject()
.startObject("field1").field("type", "keyword").endObject()
.startObject("field2").field("type", "text").endObject()
.endObject()
.endObject()
.endObject()
@ -78,4 +90,51 @@ public class RestPutIndexTemplateActionTests extends RestActionTestCase {
assertEquals(expectedContentAsMap, source);
}
public void testIncludeTypeName() throws IOException {
XContentBuilder typedContent = XContentFactory.jsonBuilder().startObject()
.startObject("mappings")
.startObject("my_doc")
.startObject("properties")
.startObject("field1").field("type", "keyword").endObject()
.startObject("field2").field("type", "text").endObject()
.endObject()
.endObject()
.endObject()
.startObject("aliases")
.startObject("read_alias").endObject()
.endObject()
.endObject();
Map<String, String> params = new HashMap<>();
params.put(INCLUDE_TYPE_NAME_PARAMETER, "true");
RestRequest request = new FakeRestRequest.Builder(xContentRegistry())
.withMethod(RestRequest.Method.PUT)
.withParams(params)
.withPath("/_template/_some_template")
.withContent(BytesReference.bytes(typedContent), XContentType.JSON)
.build();
action.prepareRequest(request, mock(NodeClient.class));
assertWarnings(RestPutIndexTemplateAction.TYPES_DEPRECATION_MESSAGE);
boolean includeTypeName = true;
Map<String, Object> source = action.prepareRequestSource(request, includeTypeName);
XContentBuilder expectedContent = XContentFactory.jsonBuilder().startObject()
.startObject("mappings")
.startObject("my_doc")
.startObject("properties")
.startObject("field1").field("type", "keyword").endObject()
.startObject("field2").field("type", "text").endObject()
.endObject()
.endObject()
.endObject()
.startObject("aliases")
.startObject("read_alias").endObject()
.endObject()
.endObject();
Map<String, Object> expectedContentAsMap = XContentHelper.convertToMap(
BytesReference.bytes(expectedContent), true, expectedContent.contentType()).v2();
assertEquals(expectedContentAsMap, source);
}
}