Browse Source

HLRC: Get Deprecation Info API (#36279)

This commit adds the Get Deprecation Info API and associated
documentation.

Relates #29827
Michael Basnight 6 years ago
parent
commit
ca09936cdf

+ 26 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/MigrationClient.java

@@ -19,6 +19,8 @@
 
 package org.elasticsearch.client;
 
+import org.elasticsearch.client.migration.DeprecationInfoRequest;
+import org.elasticsearch.client.migration.DeprecationInfoResponse;
 import org.elasticsearch.client.migration.IndexUpgradeInfoRequest;
 import org.elasticsearch.client.migration.IndexUpgradeInfoResponse;
 import org.elasticsearch.action.ActionListener;
@@ -72,4 +74,28 @@ public final class MigrationClient {
         restHighLevelClient.performRequestAsyncAndParseEntity(request, MigrationRequestConverters::migrate, options,
             BulkByScrollResponse::fromXContent, listener, Collections.emptySet());
     }
+
+    /**
+     * Get deprecation info for one or more indices
+     * @param request the request
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return the response
+     * @throws IOException in case there is a problem sending the request or parsing back the response
+     */
+    public DeprecationInfoResponse getDeprecationInfo(DeprecationInfoRequest request, RequestOptions options) throws IOException {
+        return restHighLevelClient.performRequestAndParseEntity(request, MigrationRequestConverters::getDeprecationInfo, options,
+            DeprecationInfoResponse::fromXContent, Collections.emptySet());
+    }
+
+    /**
+     * Asynchronously get deprecation info for one or more indices
+     * @param request the request
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @param listener the listener to be notified upon request completion
+     */
+    public void getDeprecationInfoAsync(DeprecationInfoRequest request, RequestOptions options,
+                                        ActionListener<DeprecationInfoResponse> listener)  {
+        restHighLevelClient.performRequestAsyncAndParseEntity(request, MigrationRequestConverters::getDeprecationInfo, options,
+            DeprecationInfoResponse::fromXContent, listener, Collections.emptySet());
+    }
 }

+ 10 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/MigrationRequestConverters.java

@@ -21,6 +21,7 @@ package org.elasticsearch.client;
 
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
+import org.elasticsearch.client.migration.DeprecationInfoRequest;
 import org.elasticsearch.client.migration.IndexUpgradeInfoRequest;
 import org.elasticsearch.client.migration.IndexUpgradeRequest;
 
@@ -48,6 +49,15 @@ final class MigrationRequestConverters {
         return prepareMigrateRequest(indexUpgradeRequest, false);
     }
 
+    static Request getDeprecationInfo(DeprecationInfoRequest deprecationInfoRequest) {
+        String endpoint = new RequestConverters.EndpointBuilder()
+            .addCommaSeparatedPathParts(deprecationInfoRequest.getIndices())
+            .addPathPartAsIs("_migration", "deprecations")
+            .build();
+
+        return new Request(HttpGet.METHOD_NAME, endpoint);
+    }
+
     private static Request prepareMigrateRequest(IndexUpgradeRequest indexUpgradeRequest, boolean waitForCompletion) {
         String endpoint = new RequestConverters.EndpointBuilder()
             .addPathPartAsIs("_migration", "upgrade")

+ 43 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/migration/DeprecationInfoRequest.java

@@ -0,0 +1,43 @@
+/*
+ * 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.migration;
+
+import org.elasticsearch.client.Validatable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class DeprecationInfoRequest implements Validatable {
+
+    private final List<String> indices;
+
+    public DeprecationInfoRequest(List<String> indices) {
+        this.indices = Collections.unmodifiableList(Objects.requireNonNull(indices, "indices cannot be null"));
+    }
+
+    public DeprecationInfoRequest() {
+        this.indices = Collections.unmodifiableList(Collections.emptyList());
+    }
+
+    public List<String> getIndices() {
+        return indices;
+    }
+}

+ 208 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/migration/DeprecationInfoResponse.java

@@ -0,0 +1,208 @@
+/*
+ * 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.migration;
+
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+public class DeprecationInfoResponse {
+
+    private static final ParseField CLUSTER_SETTINGS = new ParseField("cluster_settings");
+    private static final ParseField NODE_SETTINGS = new ParseField("node_settings");
+    private static final ParseField INDEX_SETTINGS = new ParseField("index_settings");
+
+    private final List<DeprecationIssue> clusterSettingsIssues;
+    private final List<DeprecationIssue> nodeSettingsIssues;
+    private final Map<String, List<DeprecationIssue>> indexSettingsIssues;
+
+    public DeprecationInfoResponse(List<DeprecationIssue> clusterSettingsIssues, List<DeprecationIssue> nodeSettingsIssues,
+                                   Map<String, List<DeprecationIssue>> indexSettingsIssues) {
+        this.clusterSettingsIssues = Objects.requireNonNull(clusterSettingsIssues, "cluster settings issues cannot be null");
+        this.nodeSettingsIssues = Objects.requireNonNull(nodeSettingsIssues, "node settings issues cannot be null");
+        this.indexSettingsIssues = Objects.requireNonNull(indexSettingsIssues, "index settings issues cannot be null");
+    }
+
+    public List<DeprecationIssue> getClusterSettingsIssues() {
+        return clusterSettingsIssues;
+    }
+
+    public List<DeprecationIssue> getNodeSettingsIssues() {
+        return nodeSettingsIssues;
+    }
+
+    public Map<String, List<DeprecationIssue>> getIndexSettingsIssues() {
+        return indexSettingsIssues;
+    }
+
+    private static List<DeprecationIssue> parseDeprecationIssues(XContentParser parser) throws IOException {
+        List<DeprecationIssue> issues = new ArrayList<>();
+        XContentParser.Token token = null;
+        while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
+            if (token == XContentParser.Token.START_OBJECT) {
+                issues.add(DeprecationIssue.PARSER.parse(parser, null));
+            }
+        }
+        return issues;
+    }
+
+    public static DeprecationInfoResponse fromXContent(XContentParser parser) throws IOException {
+        Map<String, List<DeprecationIssue>> indexSettings = new HashMap<>();
+        List<DeprecationIssue> clusterSettings = new ArrayList<>();
+        List<DeprecationIssue> nodeSettings = new ArrayList<>();
+        String fieldName = null;
+        XContentParser.Token token;
+        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+            if (token == XContentParser.Token.FIELD_NAME) {
+                fieldName = parser.currentName();
+            } else if (CLUSTER_SETTINGS.getPreferredName().equals(fieldName)) {
+                clusterSettings.addAll(parseDeprecationIssues(parser));
+            } else if (NODE_SETTINGS.getPreferredName().equals(fieldName)) {
+                nodeSettings.addAll(parseDeprecationIssues(parser));
+            } else if (INDEX_SETTINGS.getPreferredName().equals(fieldName)) {
+                // parse out the key/value pairs
+                while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+                    String key = parser.currentName();
+                    List<DeprecationIssue> value = parseDeprecationIssues(parser);
+                    if (value.size() > 0) { // only add indices that contain deprecation issues
+                        indexSettings.put(key, value);
+                    }
+                }
+            }
+        }
+        return new DeprecationInfoResponse(clusterSettings, nodeSettings, indexSettings);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DeprecationInfoResponse that = (DeprecationInfoResponse) o;
+        return Objects.equals(clusterSettingsIssues, that.clusterSettingsIssues) &&
+            Objects.equals(nodeSettingsIssues, that.nodeSettingsIssues) &&
+            Objects.equals(indexSettingsIssues, that.indexSettingsIssues);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(clusterSettingsIssues, nodeSettingsIssues, indexSettingsIssues);
+    }
+
+    @Override
+    public String toString() {
+        return clusterSettingsIssues.toString() + ":" + nodeSettingsIssues.toString() + ":" + indexSettingsIssues.toString();
+    }
+
+    /**
+     * Information about deprecated items
+     */
+    public static class DeprecationIssue {
+
+        private static final ParseField LEVEL = new ParseField("level");
+        private static final ParseField MESSAGE = new ParseField("message");
+        private static final ParseField URL = new ParseField("url");
+        private static final ParseField DETAILS = new ParseField("details");
+
+        static final ConstructingObjectParser<DeprecationIssue, Void> PARSER =
+            new ConstructingObjectParser<>("deprecation_issue", true,
+                a -> new DeprecationIssue(Level.fromString((String) a[0]), (String) a[1], (String) a[2], (String) a[3]));
+
+        static {
+            PARSER.declareString(ConstructingObjectParser.constructorArg(), LEVEL);
+            PARSER.declareString(ConstructingObjectParser.constructorArg(), MESSAGE);
+            PARSER.declareString(ConstructingObjectParser.constructorArg(), URL);
+            PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), DETAILS);
+        }
+
+        public enum Level {
+            NONE,
+            INFO,
+            WARNING,
+            CRITICAL
+            ;
+
+            public static Level fromString(String value) {
+                return Level.valueOf(value.toUpperCase(Locale.ROOT));
+            }
+
+            @Override
+            public String toString() {
+                return name().toLowerCase(Locale.ROOT);
+            }
+        }
+
+        private Level level;
+        private String message;
+        private String url;
+        private String details;
+
+        public DeprecationIssue(Level level, String message, String url, @Nullable String details) {
+            this.level = level;
+            this.message = message;
+            this.url = url;
+            this.details = details;
+        }
+
+        public Level getLevel() {
+            return level;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public String getUrl() {
+            return url;
+        }
+
+        public String getDetails() {
+            return details;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            DeprecationIssue that = (DeprecationIssue) o;
+            return Objects.equals(level, that.level) &&
+                Objects.equals(message, that.message) &&
+                Objects.equals(url, that.url) &&
+                Objects.equals(details, that.details);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(level, message, url, details);
+        }
+    }
+}

+ 14 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/MigrationIT.java

@@ -20,6 +20,8 @@
 package org.elasticsearch.client;
 
 import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.client.migration.DeprecationInfoRequest;
+import org.elasticsearch.client.migration.DeprecationInfoResponse;
 import org.elasticsearch.client.migration.IndexUpgradeInfoRequest;
 import org.elasticsearch.client.migration.IndexUpgradeInfoResponse;
 import org.elasticsearch.client.migration.IndexUpgradeRequest;
@@ -27,9 +29,11 @@ import org.elasticsearch.client.tasks.TaskSubmissionResponse;
 import org.elasticsearch.common.settings.Settings;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.function.BooleanSupplier;
 
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
 
 public class MigrationIT extends ESRestHighLevelClientTestCase {
 
@@ -73,6 +77,16 @@ public class MigrationIT extends ESRestHighLevelClientTestCase {
         awaitBusy(hasUpgradeCompleted);
     }
 
+    public void testGetDeprecationInfo() throws IOException {
+        createIndex("test", Settings.EMPTY);
+        DeprecationInfoRequest request = new DeprecationInfoRequest(Collections.singletonList("test"));
+        DeprecationInfoResponse response = highLevelClient().migration().getDeprecationInfo(request, RequestOptions.DEFAULT);
+        // a test like this cannot test actual deprecations
+        assertThat(response.getClusterSettingsIssues().size(), equalTo(0));
+        assertThat(response.getIndexSettingsIssues().size(), equalTo(0));
+        assertThat(response.getNodeSettingsIssues().size(), equalTo(0));
+    }
+
     /**
      * Using low-level api as high-level-rest-client's getTaskById work is in progress.
      * TODO revisit once that work is finished

+ 59 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MigrationClientDocumentationIT.java

@@ -26,6 +26,8 @@ import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.client.ESRestHighLevelClientTestCase;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.client.migration.DeprecationInfoRequest;
+import org.elasticsearch.client.migration.DeprecationInfoResponse;
 import org.elasticsearch.client.migration.IndexUpgradeInfoRequest;
 import org.elasticsearch.client.migration.IndexUpgradeInfoResponse;
 import org.elasticsearch.client.migration.IndexUpgradeRequest;
@@ -36,6 +38,8 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.reindex.BulkByScrollResponse;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -155,4 +159,59 @@ public class MigrationClientDocumentationIT extends ESRestHighLevelClientTestCas
         // end::upgrade-task-api
         assertThat(taskId, not(isEmptyOrNullString()));
     }
+
+    public void testGetDeprecationInfo() throws IOException, InterruptedException {
+        RestHighLevelClient client = highLevelClient();
+        createIndex("test", Settings.EMPTY);
+
+        //tag::get-deprecation-info-request
+        List<String> indices = new ArrayList<>();
+        indices.add("test");
+        DeprecationInfoRequest deprecationInfoRequest = new DeprecationInfoRequest(indices); // <1>
+        //end::get-deprecation-info-request
+
+        // tag::get-deprecation-info-execute
+        DeprecationInfoResponse deprecationInfoResponse =
+            client.migration().getDeprecationInfo(deprecationInfoRequest, RequestOptions.DEFAULT);
+        // end::get-deprecation-info-execute
+
+        // tag::get-deprecation-info-response
+        List<DeprecationInfoResponse.DeprecationIssue> clusterIssues =
+            deprecationInfoResponse.getClusterSettingsIssues(); // <1>
+        List<DeprecationInfoResponse.DeprecationIssue> nodeIssues =
+            deprecationInfoResponse.getNodeSettingsIssues(); // <2>
+        Map<String, List<DeprecationInfoResponse.DeprecationIssue>> indexIssues =
+            deprecationInfoResponse.getIndexSettingsIssues(); // <3>
+        // end::get-deprecation-info-response
+
+        // tag::get-deprecation-info-execute-listener
+        ActionListener<DeprecationInfoResponse> listener =
+            new ActionListener<DeprecationInfoResponse>() {
+                @Override
+                public void onResponse(DeprecationInfoResponse deprecationInfoResponse1) { // <1>
+                    List<DeprecationInfoResponse.DeprecationIssue> clusterIssues =
+                        deprecationInfoResponse.getClusterSettingsIssues();
+                    List<DeprecationInfoResponse.DeprecationIssue> nodeIssues =
+                        deprecationInfoResponse.getNodeSettingsIssues();
+                    Map<String, List<DeprecationInfoResponse.DeprecationIssue>> indexIssues =
+                        deprecationInfoResponse.getIndexSettingsIssues();
+                }
+
+                @Override
+                public void onFailure(Exception e) {
+                    // <2>
+                }
+            };
+        // end::get-deprecation-info-execute-listener
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        listener = new LatchedActionListener<>(listener, latch);
+
+        // tag::get-deprecation-info-execute-async
+        client.migration().getDeprecationInfoAsync(deprecationInfoRequest,
+            RequestOptions.DEFAULT, listener); // <1>
+        // end::get-deprecation-info-execute-async
+
+        assertTrue(latch.await(30L, TimeUnit.SECONDS));
+    }
 }

+ 146 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/migration/DeprecationInfoResponseTests.java

@@ -0,0 +1,146 @@
+/*
+ * 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.migration;
+
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.test.EqualsHashCodeTestUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester;
+
+public class DeprecationInfoResponseTests extends ESTestCase {
+
+    private void toXContent(DeprecationInfoResponse response, XContentBuilder builder) throws IOException {
+        builder.startObject();
+        {
+            builder.startArray("cluster_settings");
+            for (DeprecationInfoResponse.DeprecationIssue issue : response.getClusterSettingsIssues()) {
+                toXContent(issue, builder);
+            }
+            builder.endArray();
+
+            builder.startArray("node_settings");
+            for (DeprecationInfoResponse.DeprecationIssue issue : response.getNodeSettingsIssues()) {
+                toXContent(issue, builder);
+            }
+            builder.endArray();
+
+            builder.field("index_settings");
+            builder.startObject();
+            {
+                for (Map.Entry<String, List<DeprecationInfoResponse.DeprecationIssue>> entry :
+                        response.getIndexSettingsIssues().entrySet()) {
+                    builder.field(entry.getKey());
+                    builder.startArray();
+                    for (DeprecationInfoResponse.DeprecationIssue issue : entry.getValue()) {
+                        toXContent(issue, builder);
+                    }
+                    builder.endArray();
+                }
+            }
+            builder.endObject();
+        }
+        builder.endObject();
+    }
+
+    private void toXContent(DeprecationInfoResponse.DeprecationIssue issue, XContentBuilder builder) throws IOException {
+        builder.startObject()
+            .field("level", issue.getLevel())
+            .field("message", issue.getMessage())
+            .field("url", issue.getUrl());
+        if (issue.getDetails()!= null) {
+            builder.field("details", issue.getDetails());
+        }
+        builder.endObject();
+    }
+
+
+    private Map<String, List<DeprecationInfoResponse.DeprecationIssue>> createIndexSettingsIssues() {
+        Map<String, List<DeprecationInfoResponse.DeprecationIssue>> indexSettingsIssues =
+            new HashMap<>();
+        for (int i = 0; i < randomIntBetween(1, 3); i++) {
+            indexSettingsIssues.put(randomAlphaOfLengthBetween(1, 5), createRandomIssues(false));
+        }
+        return indexSettingsIssues;
+    }
+
+    private List<DeprecationInfoResponse.DeprecationIssue> createRandomIssues(boolean canBeEmpty) {
+        List<DeprecationInfoResponse.DeprecationIssue> list = new ArrayList<>();
+        // the list of index settings cannot be zero, but the other lists can be, so this boolean is used to make the min number
+        // of elements for this list.
+        int startingRandomNumber = canBeEmpty ? 0: 1;
+        for (int i =0; i < randomIntBetween(startingRandomNumber, 2); i++) {
+            list.add(new DeprecationInfoResponse.DeprecationIssue(DeprecationInfoResponse.DeprecationIssue.Level.INFO,
+                randomAlphaOfLength(5),
+                randomAlphaOfLength(5),
+                randomBoolean() ? randomAlphaOfLength(5) : null));
+        }
+        return list;
+    }
+
+    private DeprecationInfoResponse createInstance() {
+        return new DeprecationInfoResponse(createRandomIssues(true), createRandomIssues(true), createIndexSettingsIssues());
+    }
+
+    private DeprecationInfoResponse copyInstance(DeprecationInfoResponse req) {
+        return new DeprecationInfoResponse(new ArrayList<>(req.getClusterSettingsIssues()),
+            new ArrayList<>(req.getNodeSettingsIssues()), new HashMap<>(req.getIndexSettingsIssues()));
+    }
+
+    private DeprecationInfoResponse mutateInstance(DeprecationInfoResponse req) {
+        return createInstance();
+    }
+
+    public void testFromXContent() throws IOException {
+        xContentTester(
+            this::createParser,
+            this::createInstance,
+            this::toXContent,
+            DeprecationInfoResponse::fromXContent)
+            .supportsUnknownFields(false) // old school parsing
+            .test();
+    }
+
+    public void testNullFailedIndices() {
+        NullPointerException exception =
+            expectThrows(NullPointerException.class, () -> new DeprecationInfoResponse(null, null, null));
+        assertEquals("cluster settings issues cannot be null", exception.getMessage());
+
+        exception = expectThrows(NullPointerException.class, () -> new DeprecationInfoResponse(Collections.emptyList(), null, null));
+        assertEquals("node settings issues cannot be null", exception.getMessage());
+
+        exception = expectThrows(NullPointerException.class,
+            () -> new DeprecationInfoResponse(Collections.emptyList(), Collections.emptyList(), null));
+        assertEquals("index settings issues cannot be null", exception.getMessage());
+    }
+
+    public void testEqualsAndHashCode() {
+        for (int count = 0; count < 100; ++count) {
+            EqualsHashCodeTestUtils.checkEqualsAndHashCode(createInstance(), this::copyInstance, this::mutateInstance);
+        }
+    }
+}

+ 35 - 0
docs/java-rest/high-level/migration/get-deprecation-info.asciidoc

@@ -0,0 +1,35 @@
+--
+:api: get-deprecation-info
+:request: DeprecationInfoRequest
+:response: DeprecationInfoResponse
+--
+
+[id="{upid}-{api}"]
+=== Get Deprecation Info
+
+[id="{upid}-{api}-request"]
+==== Get Deprecation Info Request
+
+A +{request}+ can be applied to one or more indices:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request]
+--------------------------------------------------
+<1> Create a new request instance
+
+include::../execution.asciidoc[]
+
+[id="{upid}-{api}-response"]
+==== Get Deprecation Info Response
+
+The returned +{response}+ contains information about deprecated features currently
+in use at the cluster, node, and index level.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-response]
+--------------------------------------------------
+<1> a List of Cluster deprecations
+<2> a List of Node deprecations
+<3> a Map of key IndexName, value List of deprecations for the index

+ 0 - 1
docs/java-rest/high-level/migration/upgrade.asciidoc

@@ -3,7 +3,6 @@
 :request: IndexUpgradeRequest
 :response: BulkByScrollResponse
 :submit_response: IndexUpgradeSubmissionResponse
-:doc-tests-file: {doc-tests}/MigrationClientDocumentationIT.java
 --
 
 [[java-rest-high-migration-upgrade]]

+ 6 - 1
docs/java-rest/high-level/supported-apis.asciidoc

@@ -334,13 +334,18 @@ include::ml/delete-expired-data.asciidoc[]
 
 == Migration APIs
 
+:upid: {mainid}-migration
+:doc-tests-file: {doc-tests}/MigrationClientDocumentationIT.java
+
 The Java High Level REST Client supports the following Migration APIs:
 
 * <<java-rest-high-migration-get-assistance>>
-* <<java-rest-high-migration-upgrade>>
+* <<{upid}-upgrade>>
+* <<{upid}-get-deprecation-info>>
 
 include::migration/get-assistance.asciidoc[]
 include::migration/upgrade.asciidoc[]
+include::migration/get-deprecation-info.asciidoc[]
 
 == Rollup APIs