Make it possible to deprecate all variants of a ParseField with no replacement (#53722)

Sometimes we want to deprecate and remove a ParseField entirely, without replacement;
for example, the various places where we specify a _type field in 7x. Currently we can
tell users only that a particular field name should not be used, and that another name should
be used in its place. This commit adds the ability to say that a field should not be used at
all.
This commit is contained in:
Alan Woodward 2020-03-18 14:15:30 +00:00
parent d56dee599a
commit 580bc40c0c
15 changed files with 128 additions and 120 deletions

View File

@ -1888,12 +1888,7 @@ public class RestHighLevelClient implements Closeable {
* emitted there just mean that you are talking to an old version of
* Elasticsearch. There isn't anything you can do about the deprecation.
*/
private static final DeprecationHandler DEPRECATION_HANDLER = new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {}
};
private static final DeprecationHandler DEPRECATION_HANDLER = DeprecationHandler.IGNORE_DEPRECATIONS;
static List<NamedXContentRegistry.Entry> getDefaultNamedXContents() {
Map<String, ContextParser<Object, ? extends Aggregation>> map = new HashMap<>();

View File

@ -50,7 +50,7 @@ public class RuleScope implements ToXContentObject {
Map<String, ?> value = (Map<String, ?>) entry.getValue();
builder.map(value);
try (XContentParser scopeParser = XContentFactory.xContent(builder.contentType()).createParser(
NamedXContentRegistry.EMPTY, DEPRECATION_HANDLER, Strings.toString(builder))) {
NamedXContentRegistry.EMPTY, DeprecationHandler.IGNORE_DEPRECATIONS, Strings.toString(builder))) {
scope.put(entry.getKey(), FilterRef.PARSER.parse(scopeParser, null));
}
}
@ -59,15 +59,6 @@ public class RuleScope implements ToXContentObject {
};
}
private static final DeprecationHandler DEPRECATION_HANDLER = new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {}
};
private final Map<String, FilterRef> scope;
public RuleScope() {

View File

@ -35,15 +35,7 @@ public class DeleteRoleMappingResponseTests extends ESTestCase {
public void testFromXContent() throws IOException {
final String json = "{ \"found\" : \"true\" }";
final DeleteRoleMappingResponse response = DeleteRoleMappingResponse.fromXContent(XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json));
new NamedXContentRegistry(Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json));
final DeleteRoleMappingResponse expectedResponse = new DeleteRoleMappingResponse(true);
assertThat(response, equalTo(expectedResponse));
}

View File

@ -37,29 +37,21 @@ import static org.hamcrest.Matchers.equalTo;
public class ExpressionRoleMappingTests extends ESTestCase {
public void testExpressionRoleMappingParser() throws IOException {
final String json =
"{\n" +
" \"enabled\" : true,\n" +
" \"roles\" : [\n" +
" \"superuser\"\n" +
" ],\n" +
" \"rules\" : {\n" +
" \"field\" : {\n" +
" \"realm.name\" : \"kerb1\"\n" +
" }\n" +
" },\n" +
" \"metadata\" : { }\n" +
final String json =
"{\n" +
" \"enabled\" : true,\n" +
" \"roles\" : [\n" +
" \"superuser\"\n" +
" ],\n" +
" \"rules\" : {\n" +
" \"field\" : {\n" +
" \"realm.name\" : \"kerb1\"\n" +
" }\n" +
" },\n" +
" \"metadata\" : { }\n" +
" }";
final ExpressionRoleMapping expressionRoleMapping = ExpressionRoleMapping.PARSER.parse(XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json), "example-role-mapping");
new NamedXContentRegistry(Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json), "example-role-mapping");
final ExpressionRoleMapping expectedRoleMapping = new ExpressionRoleMapping("example-role-mapping",
FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"),
singletonList("superuser"), Collections.emptyList(),

View File

@ -80,15 +80,7 @@ public class GetPrivilegesResponseTests extends ESTestCase {
"}";
final GetPrivilegesResponse response = GetPrivilegesResponse.fromXContent(XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json));
new NamedXContentRegistry(Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json));
final ApplicationPrivilege readTestappPrivilege =
new ApplicationPrivilege("testapp", "read", Arrays.asList("action:login", "data:read/*"), null);

View File

@ -36,42 +36,34 @@ import static org.hamcrest.Matchers.equalTo;
public class GetRoleMappingsResponseTests extends ESTestCase {
public void testFromXContent() throws IOException {
final String json = "{\n" +
" \"kerberosmapping\" : {\n" +
" \"enabled\" : true,\n" +
" \"roles\" : [\n" +
" \"superuser\"\n" +
" ],\n" +
" \"rules\" : {\n" +
" \"field\" : {\n" +
" \"realm.name\" : \"kerb1\"\n" +
" }\n" +
" },\n" +
" \"metadata\" : { }\n" +
" },\n" +
" \"ldapmapping\" : {\n" +
" \"enabled\" : false,\n" +
" \"roles\" : [\n" +
" \"monitoring\"\n" +
" ],\n" +
" \"rules\" : {\n" +
" \"field\" : {\n" +
" \"groups\" : \"cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local\"\n" +
" }\n" +
" },\n" +
" \"metadata\" : { }\n" +
" }\n" +
final String json = "{\n" +
" \"kerberosmapping\" : {\n" +
" \"enabled\" : true,\n" +
" \"roles\" : [\n" +
" \"superuser\"\n" +
" ],\n" +
" \"rules\" : {\n" +
" \"field\" : {\n" +
" \"realm.name\" : \"kerb1\"\n" +
" }\n" +
" },\n" +
" \"metadata\" : { }\n" +
" },\n" +
" \"ldapmapping\" : {\n" +
" \"enabled\" : false,\n" +
" \"roles\" : [\n" +
" \"monitoring\"\n" +
" ],\n" +
" \"rules\" : {\n" +
" \"field\" : {\n" +
" \"groups\" : \"cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local\"\n" +
" }\n" +
" },\n" +
" \"metadata\" : { }\n" +
" }\n" +
"}";
final GetRoleMappingsResponse response = GetRoleMappingsResponse.fromXContent(XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json));
new NamedXContentRegistry(Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json));
final List<ExpressionRoleMapping> expectedRoleMappingsList = new ArrayList<>();
expectedRoleMappingsList.add(new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name",
"kerb1"), Collections.singletonList("superuser"), Collections.emptyList(), null, true));

View File

@ -64,15 +64,7 @@ public class GetRolesResponseTests extends ESTestCase {
" }\n" +
"}";
final GetRolesResponse response = GetRolesResponse.fromXContent((XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json)));
new NamedXContentRegistry(Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json)));
assertThat(response.getRoles().size(), equalTo(1));
assertThat(response.getTransientMetadataMap().size(), equalTo(1));
final Role role = response.getRoles().get(0);

View File

@ -115,15 +115,7 @@ public class RoleMapperExpressionParserTests extends ESTestCase {
private RoleMapperExpression parse(String json) throws IOException {
return new RoleMapperExpressionParser().parse("rules", XContentType.JSON.xContent().createParser(new NamedXContentRegistry(
Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json));
Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json));
}
}

View File

@ -54,15 +54,7 @@ public class ApplicationPrivilegeTests extends ESTestCase {
+ " }\n"
+ "}";
final ApplicationPrivilege privilege = ApplicationPrivilege.fromXContent(XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
}, json));
new NamedXContentRegistry(Collections.emptyList()), DeprecationHandler.IGNORE_DEPRECATIONS, json));
final Map<String, Object> metadata = new HashMap<>();
metadata.put("description", "Read access to myapp");
final ApplicationPrivilege expectedPrivilege =

View File

@ -34,6 +34,7 @@ public class ParseField {
private final String[] deprecatedNames;
private String allReplacedWith = null;
private final String[] allNames;
private boolean fullyDeprecated = false;
private static final String[] EMPTY = new String[0];
@ -96,6 +97,15 @@ public class ParseField {
return parseField;
}
/**
* Return a new ParseField where all field names are deprecated with no replacement
*/
public ParseField withAllDeprecated() {
ParseField parseField = this.withDeprecation(getAllNamesIncludedDeprecated());
parseField.fullyDeprecated = true;
return parseField;
}
/**
* Does {@code fieldName} match this field?
* @param fieldName
@ -108,7 +118,7 @@ public class ParseField {
Objects.requireNonNull(fieldName, "fieldName cannot be null");
// if this parse field has not been completely deprecated then try to
// match the preferred name
if (allReplacedWith == null && fieldName.equals(name)) {
if (fullyDeprecated == false && allReplacedWith == null && fieldName.equals(name)) {
return true;
}
// Now try to match against one of the deprecated names. Note that if
@ -116,7 +126,9 @@ public class ParseField {
// fields will be in the deprecatedNames array
for (String depName : deprecatedNames) {
if (fieldName.equals(depName)) {
if (allReplacedWith == null) {
if (fullyDeprecated) {
deprecationHandler.usedDeprecatedField(fieldName);
} else if (allReplacedWith == null) {
deprecationHandler.usedDeprecatedName(fieldName, name);
} else {
deprecationHandler.usedDeprecatedField(fieldName, allReplacedWith);

View File

@ -41,6 +41,32 @@ public interface DeprecationHandler {
throw new UnsupportedOperationException("deprecated fields not supported here but got ["
+ usedName + "] which has been replaced with [" + modernName + "]");
}
@Override
public void usedDeprecatedField(String usedName) {
throw new UnsupportedOperationException("deprecated fields not supported here but got ["
+ usedName + "] which has been deprecated entirely");
}
};
/**
* Ignores all deprecations
*/
DeprecationHandler IGNORE_DEPRECATIONS = new DeprecationHandler() {
@Override
public void usedDeprecatedName(String usedName, String modernName) {
}
@Override
public void usedDeprecatedField(String usedName, String replacedWith) {
}
@Override
public void usedDeprecatedField(String usedName) {
}
};
/**
@ -52,9 +78,16 @@ public interface DeprecationHandler {
/**
* Called when the provided field name matches the current field but the entire
* field has been marked as deprecated.
* field has been marked as deprecated and another field should be used
* @param usedName the provided field name
* @param replacedWith the name of the field that replaced this field
*/
void usedDeprecatedField(String usedName, String replacedWith);
/**
* Called when the provided field name matches the current field but the entire
* field has been marked as deprecated with no replacement
* @param usedName the provided field name
*/
void usedDeprecatedField(String usedName);
}

View File

@ -60,6 +60,21 @@ public class ParseFieldTests extends ESTestCase {
assertWarnings("Deprecated field [like_text] used, replaced by [like]");
}
public void testDeprecatedWithNoReplacement() {
String name = "dep";
String[] alternatives = new String[]{"old_dep", "new_dep"};
ParseField field = new ParseField(name).withDeprecation(alternatives).withAllDeprecated();
assertFalse(field.match("not a field name", LoggingDeprecationHandler.INSTANCE));
assertTrue(field.match("dep", LoggingDeprecationHandler.INSTANCE));
assertWarnings("Deprecated field [dep] used, which has been removed entirely");
assertTrue(field.match("old_dep", LoggingDeprecationHandler.INSTANCE));
assertWarnings("Deprecated field [old_dep] used, which has been removed entirely");
assertTrue(field.match("new_dep", LoggingDeprecationHandler.INSTANCE));
assertWarnings("Deprecated field [new_dep] used, which has been removed entirely");
}
public void testGetAllNamesIncludedDeprecated() {
ParseField parseField = new ParseField("terms", "in");
assertThat(parseField.getAllNamesIncludedDeprecated(), arrayContainingInAnyOrder("terms", "in"));

View File

@ -33,7 +33,7 @@ import org.elasticsearch.common.logging.DeprecationLogger;
* though the user sent them.
*/
public class LoggingDeprecationHandler implements DeprecationHandler {
public static LoggingDeprecationHandler INSTANCE = new LoggingDeprecationHandler();
public static final LoggingDeprecationHandler INSTANCE = new LoggingDeprecationHandler();
/**
* The logger to which to send deprecation messages.
*
@ -57,4 +57,9 @@ public class LoggingDeprecationHandler implements DeprecationHandler {
public void usedDeprecatedField(String usedName, String replacedWith) {
deprecationLogger.deprecated("Deprecated field [{}] used, replaced by [{}]", usedName, replacedWith);
}
@Override
public void usedDeprecatedField(String usedName) {
deprecationLogger.deprecated("Deprecated field [{}] used, which has been removed entirely", usedName);
}
}

View File

@ -39,6 +39,13 @@ public class LoggingDeprecationAccumulationHandler implements DeprecationHandler
new Object[] {usedName, replacedWith}));
}
@Override
public void usedDeprecatedField(String usedName) {
LoggingDeprecationHandler.INSTANCE.usedDeprecatedField(usedName);
deprecations.add(LoggerMessageFormat.format("Deprecated field [{}] used, which has been deprecated entirely",
new Object[]{ usedName }));
}
/**
* The collected deprecation warnings
*/

View File

@ -634,6 +634,12 @@ public class ApiKeyService {
deprecationLogger.deprecated("Deprecated field [{}] used in api key [{}], replaced by [{}]",
usedName, apiKeyId, replacedWith);
}
@Override
public void usedDeprecatedField(String usedName) {
deprecationLogger.deprecated("Deprecated field [{}] used in api key [{}], which has been removed entirely",
usedName);
}
}
/**