Skip to content

Commit 86500ae

Browse files
authored
Add explicit tests for dynamic template validation on bad mapping parameters (#74177)
When a dynamic template is applied to a 7.x index, we do some validation checks against the generated mappings and issue a deprecation warning if the validation fails. We had some tests that were checking this at a low level, but nothing that exercised the full logic. When dynamic runtimes were added, we expected this behaviour to carry over; however, a bug in building the ParserContext to pass to the runtime Builder meant that we would instead always throw an error. In fact, erroring here is a good result, as the only reason we issue a deprecation warning for non-runtime fields is to preserve backward compatibility; given that runtime fields are new, it has never been possible to add a bad dynamic runtime template. This commit adds specific tests for both situations. It ensures that the parser context passed to a runtime builder will know that it is in a dynamic context, but it also removes the version check and deprecation warning, as they were never actually triggered and we can be stricter here.
1 parent c2e8625 commit 86500ae

File tree

6 files changed

+89
-28
lines changed

6 files changed

+89
-28
lines changed

server/src/main/java/org/elasticsearch/index/mapper/DynamicFieldsBuilder.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private static boolean applyMatchingTemplate(ParseContext context,
193193
String mappingType = dynamicTemplate.mappingType(dynamicType);
194194
Map<String, Object> mapping = dynamicTemplate.mappingForName(name, dynamicType);
195195
if (dynamicTemplate.isRuntimeMapping()) {
196-
Mapper.TypeParser.ParserContext parserContext = context.parserContext(dateFormatter);
196+
Mapper.TypeParser.ParserContext parserContext = context.dynamicTemplateParserContext(dateFormatter);
197197
RuntimeField.Parser parser = parserContext.runtimeFieldParser(mappingType);
198198
String fullName = context.path().pathAsText(name);
199199
if (parser == null) {
@@ -225,8 +225,7 @@ private static Mapper.Builder parseDynamicTemplateMapping(String name,
225225
Map<String, Object> mapping,
226226
DateFormatter dateFormatter,
227227
ParseContext context) {
228-
Mapper.TypeParser.ParserContext parserContext = context.parserContext(dateFormatter);
229-
parserContext = parserContext.createDynamicTemplateFieldContext(parserContext);
228+
Mapper.TypeParser.ParserContext parserContext = context.dynamicTemplateParserContext(dateFormatter);
230229
Mapper.TypeParser typeParser = parserContext.typeParser(mappingType);
231230
if (typeParser == null) {
232231
throw new MapperParsingException("failed to find type parsed [" + mappingType + "] for [" + name + "]");

server/src/main/java/org/elasticsearch/index/mapper/Mapper.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ ParserContext createMultiFieldContext(ParserContext in) {
144144
return new MultiFieldParserContext(in);
145145
}
146146

147-
ParserContext createDynamicTemplateFieldContext(ParserContext in) {
148-
return new DynamicTemplateParserContext(in);
147+
ParserContext createDynamicTemplateFieldContext() {
148+
return new DynamicTemplateParserContext(this);
149149
}
150150

151151
private static class MultiFieldParserContext extends ParserContext {

server/src/main/java/org/elasticsearch/index/mapper/ParseContext.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ public Iterable<Document> nonRootDocuments() {
164164
}
165165

166166
@Override
167-
public Mapper.TypeParser.ParserContext parserContext(DateFormatter dateFormatter) {
168-
return in.parserContext(dateFormatter);
167+
public Mapper.TypeParser.ParserContext dynamicTemplateParserContext(DateFormatter dateFormatter) {
168+
return in.dynamicTemplateParserContext(dateFormatter);
169169
}
170170

171171
@Override
@@ -336,8 +336,8 @@ public InternalParseContext(MappingLookup mappingLookup,
336336
}
337337

338338
@Override
339-
public Mapper.TypeParser.ParserContext parserContext(DateFormatter dateFormatter) {
340-
return parserContextFunction.apply(dateFormatter);
339+
public Mapper.TypeParser.ParserContext dynamicTemplateParserContext(DateFormatter dateFormatter) {
340+
return parserContextFunction.apply(dateFormatter).createDynamicTemplateFieldContext();
341341
}
342342

343343
@Override
@@ -548,7 +548,7 @@ public Collection<String> getFieldNames() {
548548
*/
549549
public abstract Collection<String> getFieldNames();
550550

551-
public abstract Mapper.TypeParser.ParserContext parserContext(DateFormatter dateFormatter);
551+
public abstract Mapper.TypeParser.ParserContext dynamicTemplateParserContext(DateFormatter dateFormatter);
552552

553553
/**
554554
* Return a new context that will be within a copy-to operation.

server/src/main/java/org/elasticsearch/index/mapper/RuntimeField.java

-17
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88

99
package org.elasticsearch.index.mapper;
1010

11-
import org.elasticsearch.Version;
12-
import org.elasticsearch.common.logging.DeprecationCategory;
13-
import org.elasticsearch.common.logging.DeprecationLogger;
1411
import org.elasticsearch.common.xcontent.ToXContent;
1512
import org.elasticsearch.common.xcontent.ToXContentFragment;
1613
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -67,8 +64,6 @@ abstract class Builder implements ToXContent {
6764
final String name;
6865
final Parameter<Map<String, String>> meta = Parameter.metaParam();
6966

70-
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(RuntimeField.class);
71-
7267
protected Builder(String name) {
7368
this.name = name;
7469
}
@@ -104,18 +99,6 @@ public final void parse(String name, Mapper.TypeParser.ParserContext parserConte
10499
final Object propNode = entry.getValue();
105100
Parameter<?> parameter = paramsMap.get(propName);
106101
if (parameter == null) {
107-
if (parserContext.isFromDynamicTemplate() && parserContext.indexVersionCreated().before(Version.V_8_0_0)) {
108-
// The parameter is unknown, but this mapping is from a dynamic template.
109-
// Until 7.x it was possible to use unknown parameters there, so for bwc we need to ignore it
110-
deprecationLogger.deprecate(DeprecationCategory.API, propName,
111-
"Parameter [{}] is used in a dynamic template mapping and has no effect on type [{}]. "
112-
+ "Usage will result in an error in future major versions and should be removed.",
113-
propName,
114-
type
115-
);
116-
iterator.remove();
117-
continue;
118-
}
119102
throw new MapperParsingException(
120103
"unknown parameter [" + propName + "] on runtime field [" + name + "] of type [" + type + "]"
121104
);

server/src/test/java/org/elasticsearch/index/mapper/DynamicTemplatesTests.java

+79
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
import org.apache.lucene.index.IndexOptions;
1212
import org.apache.lucene.index.IndexableField;
1313
import org.apache.lucene.util.BytesRef;
14+
import org.elasticsearch.Version;
1415
import org.elasticsearch.index.mapper.ParseContext.Document;
16+
import org.elasticsearch.test.VersionUtils;
17+
18+
import java.io.IOException;
1519

1620
import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath;
21+
import static org.hamcrest.Matchers.containsString;
1722
import static org.hamcrest.Matchers.equalTo;
1823
import static org.hamcrest.Matchers.notNullValue;
1924

@@ -159,4 +164,78 @@ public void testSimpleWithXContentTraverse() throws Exception {
159164
fieldMapper = mapperService.documentMapper().mappers().getMapper("multi2.org");
160165
assertNotNull(fieldMapper);
161166
}
167+
168+
public void testDynamicMapperWithBadMapping() throws IOException {
169+
{
170+
// in 7.x versions this will issue a deprecation warning
171+
Version version = VersionUtils.randomCompatibleVersion(random(), Version.V_7_0_0);
172+
DocumentMapper mapper = createDocumentMapper(version, topMapping(b -> {
173+
b.startArray("dynamic_templates");
174+
{
175+
b.startObject();
176+
{
177+
b.startObject("test");
178+
{
179+
b.field("match_mapping_type", "string");
180+
b.startObject("mapping").field("badparam", false).endObject();
181+
}
182+
b.endObject();
183+
}
184+
b.endObject();
185+
}
186+
b.endArray();
187+
}));
188+
assertWarnings(
189+
"dynamic template [test] has invalid content [{\"match_mapping_type\":\"string\",\"mapping\":{\"badparam\":false}}], " +
190+
"attempted to validate it with the following match_mapping_type: [string], last error: " +
191+
"[unknown parameter [badparam] on mapper [__dynamic__test] of type [null]]");
192+
193+
mapper.parse(source(b -> b.field("field", "foo")));
194+
assertWarnings("Parameter [badparam] is used in a dynamic template mapping and has no effect on type [null]. " +
195+
"Usage will result in an error in future major versions and should be removed.");
196+
}
197+
198+
{
199+
// in 8.x it will error out
200+
Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(topMapping(b -> {
201+
b.startArray("dynamic_templates");
202+
{
203+
b.startObject();
204+
{
205+
b.startObject("test");
206+
{
207+
b.field("match_mapping_type", "string");
208+
b.startObject("mapping").field("badparam", false).endObject();
209+
}
210+
b.endObject();
211+
}
212+
b.endObject();
213+
}
214+
b.endArray();
215+
})));
216+
assertThat(e.getMessage(), containsString("dynamic template [test] has invalid content"));
217+
assertThat(e.getCause().getMessage(), containsString("badparam"));
218+
}
219+
}
220+
221+
public void testDynamicRuntimeWithBadMapping() {
222+
Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(topMapping(b -> {
223+
b.startArray("dynamic_templates");
224+
{
225+
b.startObject();
226+
{
227+
b.startObject("test");
228+
{
229+
b.field("match_mapping_type", "string");
230+
b.startObject("runtime").field("badparam", false).endObject();
231+
}
232+
b.endObject();
233+
}
234+
b.endObject();
235+
}
236+
b.endArray();
237+
})));
238+
assertThat(e.getMessage(), containsString("dynamic template [test] has invalid content"));
239+
assertThat(e.getCause().getMessage(), containsString("badparam"));
240+
}
162241
}

server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ private static TestMapper fromMapping(String mapping, Version version, boolean f
208208
throw new UnsupportedOperationException();
209209
});
210210
if (fromDynamicTemplate) {
211-
pc = pc.createDynamicTemplateFieldContext(pc);
211+
pc = pc.createDynamicTemplateFieldContext();
212212
}
213213
return (TestMapper) new TypeParser()
214214
.parse("field", XContentHelper.convertToMap(JsonXContent.jsonXContent, mapping, true), pc)

0 commit comments

Comments
 (0)