From 2a41c9f05f69c398b4f5fab7205a9fc885a7bcad Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Wed, 28 Nov 2018 11:58:20 +0100 Subject: [PATCH] [HLRC] XPack ML info action (#35777) Relates to #29827 --- .../client/MLRequestConverters.java | 8 +++ .../client/MachineLearningClient.java | 40 ++++++++++++ .../client/ml/MlInfoRequest.java | 25 ++++++++ .../client/ml/MlInfoResponse.java | 61 +++++++++++++++++++ .../client/MLRequestConvertersTests.java | 11 ++++ .../client/MachineLearningIT.java | 13 ++++ .../MlClientDocumentationIT.java | 51 ++++++++++++++++ .../java-rest/high-level/ml/get-info.asciidoc | 33 ++++++++++ .../high-level/supported-apis.asciidoc | 2 + .../ml/action/MlInfoActionResponseTests.java | 24 +++++++- 10 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoResponse.java create mode 100644 docs/java-rest/high-level/ml/get-info.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java index f3844566f6e05..8e11151667bc5 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java @@ -53,6 +53,7 @@ import org.elasticsearch.client.ml.GetModelSnapshotsRequest; import org.elasticsearch.client.ml.GetOverallBucketsRequest; import org.elasticsearch.client.ml.GetRecordsRequest; +import org.elasticsearch.client.ml.MlInfoRequest; import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.PostCalendarEventRequest; import org.elasticsearch.client.ml.PostDataRequest; @@ -663,6 +664,13 @@ static Request deleteFilter(DeleteFilterRequest deleteFilterRequest) { return request; } + static Request mlInfo(MlInfoRequest infoRequest) { + String endpoint = new EndpointBuilder() + .addPathPartAsIs("_xpack", "ml", "info") + .build(); + return new Request(HttpGet.METHOD_NAME, endpoint); + } + static Request findFileStructure(FindFileStructureRequest findFileStructureRequest) { String endpoint = new EndpointBuilder() .addPathPartAsIs("_xpack") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java index fa204c5bccedd..aaff35a238998 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java @@ -65,6 +65,8 @@ import org.elasticsearch.client.ml.GetOverallBucketsResponse; import org.elasticsearch.client.ml.GetRecordsRequest; import org.elasticsearch.client.ml.GetRecordsResponse; +import org.elasticsearch.client.ml.MlInfoRequest; +import org.elasticsearch.client.ml.MlInfoResponse; import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobResponse; import org.elasticsearch.client.ml.PostCalendarEventRequest; @@ -1758,6 +1760,44 @@ public void deleteFilterAsync(DeleteFilterRequest request, RequestOptions option Collections.emptySet()); } + /** + * Gets Machine Learning information about default values and limits. + *

+ * For additional info + * see Machine Learning info + * + * @param request The request of Machine Learning info + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return response info about default values and limits + * @throws IOException when there is a serialization issue sending the request or receiving the response + */ + public MlInfoResponse getMlInfo(MlInfoRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + MLRequestConverters::mlInfo, + options, + MlInfoResponse::fromXContent, + Collections.emptySet()); + } + + /** + * Gets Machine Learning information about default values and limits, asynchronously. + *

+ * For additional info + * see Machine Learning info + * + * @param request The request of Machine Learning info + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener Listener to be notified upon request completion + */ + public void getMlInfoAsync(MlInfoRequest request, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + MLRequestConverters::mlInfo, + options, + MlInfoResponse::fromXContent, + listener, + Collections.emptySet()); + } + /** * Finds the structure of a file *

diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoRequest.java new file mode 100644 index 0000000000000..19b6e7799c804 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoRequest.java @@ -0,0 +1,25 @@ +/* + * 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.ml; + +import org.elasticsearch.client.Validatable; + +public class MlInfoRequest implements Validatable { +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoResponse.java new file mode 100644 index 0000000000000..41f21efbe3d65 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/MlInfoResponse.java @@ -0,0 +1,61 @@ +/* + * 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.ml; + +import org.elasticsearch.client.Validatable; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; + +public class MlInfoResponse implements Validatable { + private final Map info; + + private MlInfoResponse(Map info) { + this.info = info; + } + + public Map getInfo() { + return info; + } + + public static MlInfoResponse fromXContent(XContentParser parser) throws IOException { + Map info = parser.map(); + return new MlInfoResponse(info); + } + + @Override + public int hashCode() { + return Objects.hash(info); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + MlInfoResponse other = (MlInfoResponse) obj; + return Objects.equals(info, other.info); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java index 107f4614505b2..98cd3761e5a3c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java @@ -50,6 +50,7 @@ import org.elasticsearch.client.ml.GetModelSnapshotsRequest; import org.elasticsearch.client.ml.GetOverallBucketsRequest; import org.elasticsearch.client.ml.GetRecordsRequest; +import org.elasticsearch.client.ml.MlInfoRequest; import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.PostCalendarEventRequest; import org.elasticsearch.client.ml.PostDataRequest; @@ -728,6 +729,16 @@ public void testDeleteFilter() { assertNull(request.getEntity()); } + public void testMlInfo() { + MlInfoRequest infoRequest = new MlInfoRequest(); + + Request request = MLRequestConverters.mlInfo(infoRequest); + + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertThat(request.getEndpoint(), equalTo("/_xpack/ml/info")); + assertNull(request.getEntity()); + } + public void testFindFileStructure() throws Exception { String sample = randomAlphaOfLength(randomIntBetween(1000, 2000)); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java index 4bc4f3641ac09..73c146c954f62 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java @@ -63,6 +63,8 @@ import org.elasticsearch.client.ml.GetJobStatsResponse; import org.elasticsearch.client.ml.GetModelSnapshotsRequest; import org.elasticsearch.client.ml.GetModelSnapshotsResponse; +import org.elasticsearch.client.ml.MlInfoRequest; +import org.elasticsearch.client.ml.MlInfoResponse; import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobResponse; import org.elasticsearch.client.ml.PostCalendarEventRequest; @@ -137,6 +139,7 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; public class MachineLearningIT extends ESRestHighLevelClientTestCase { @@ -1282,6 +1285,16 @@ public void testDeleteFilter() throws Exception { assertThat(exception.status().getStatus(), equalTo(404)); } + public void testGetMlInfo() throws Exception { + MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); + + MlInfoResponse infoResponse = execute(new MlInfoRequest(), machineLearningClient::getMlInfo, machineLearningClient::getMlInfoAsync); + Map info = infoResponse.getInfo(); + assertThat(info, notNullValue()); + assertTrue(info.containsKey("defaults")); + assertTrue(info.containsKey("limits")); + } + public static String randomValidJobId() { CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray()); return generator.ofCodePointsLength(random(), 10, 10); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java index e0fbf47281f26..0ef6c8d682806 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java @@ -78,6 +78,8 @@ import org.elasticsearch.client.ml.GetOverallBucketsResponse; import org.elasticsearch.client.ml.GetRecordsRequest; import org.elasticsearch.client.ml.GetRecordsResponse; +import org.elasticsearch.client.ml.MlInfoRequest; +import org.elasticsearch.client.ml.MlInfoResponse; import org.elasticsearch.client.ml.OpenJobRequest; import org.elasticsearch.client.ml.OpenJobResponse; import org.elasticsearch.client.ml.PostCalendarEventRequest; @@ -3003,6 +3005,55 @@ public void onFailure(Exception e) { } } + public void testGetMlInfo() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + // tag::get-ml-info-request + MlInfoRequest request = new MlInfoRequest(); // <1> + // end::get-ml-info-request + + // tag::get-ml-info-execute + MlInfoResponse response = client.machineLearning() + .getMlInfo(request, RequestOptions.DEFAULT); + // end::get-ml-info-execute + + // tag::get-ml-info-response + final Map info = response.getInfo();// <1> + // end::get-ml-info-response + assertTrue(info.containsKey("defaults")); + assertTrue(info.containsKey("limits")); + } + { + MlInfoRequest request = new MlInfoRequest(); + + // tag::get-ml-info-execute-listener + ActionListener listener = new ActionListener() { + @Override + public void onResponse(MlInfoResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-ml-info-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::get-ml-info-execute-async + client.machineLearning() + .getMlInfoAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::get-ml-info-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + private String createFilter(RestHighLevelClient client) throws IOException { MlFilter.Builder filterBuilder = MlFilter.builder("my_safe_domains") .setDescription("A list of safe domains") diff --git a/docs/java-rest/high-level/ml/get-info.asciidoc b/docs/java-rest/high-level/ml/get-info.asciidoc new file mode 100644 index 0000000000000..42da753329415 --- /dev/null +++ b/docs/java-rest/high-level/ml/get-info.asciidoc @@ -0,0 +1,33 @@ +-- +:api: get-ml-info +:request: MlInfoRequest +:response: MlInfoResponse +-- +[id="{upid}-{api}"] +=== ML Get Info API + +The ML Get API provides defaults and limits used internally by {ml}. +These may be useful to a user interface that needs to interpret machine learning +configurations where certain fields are missing because the end user was happy with the default value. + +It accepts a +{request}+ object and responds with a +{response}+ object. + +[id="{upid}-{api}-request"] +==== Get Info Request + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> Constructing a new request + +[id="{upid}-{api}-response"] +==== ML Get Info Response + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> `info` from the +{response}+ contains ml info details + +include::../execution.asciidoc[] diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index f4512dafd8eb8..8181dbee1e567 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -284,6 +284,7 @@ The Java High Level REST Client supports the following Machine Learning APIs: * <<{upid}-delete-model-snapshot>> * <<{upid}-revert-model-snapshot>> * <<{upid}-update-model-snapshot>> +* <<{upid}-get-ml-info>> * <<{upid}-delete-expired-data>> include::ml/put-job.asciidoc[] @@ -326,6 +327,7 @@ include::ml/get-model-snapshots.asciidoc[] include::ml/delete-model-snapshot.asciidoc[] include::ml/revert-model-snapshot.asciidoc[] include::ml/update-model-snapshot.asciidoc[] +include::ml/get-info.asciidoc[] include::ml/delete-expired-data.asciidoc[] == Migration APIs diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/MlInfoActionResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/MlInfoActionResponseTests.java index 7fb216d445d15..fe59810b595a4 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/MlInfoActionResponseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/MlInfoActionResponseTests.java @@ -5,13 +5,33 @@ */ package org.elasticsearch.xpack.core.ml.action; -import org.elasticsearch.test.AbstractStreamableTestCase; +import org.elasticsearch.client.ml.MlInfoResponse; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.protocol.AbstractHlrcStreamableXContentTestCase; import org.elasticsearch.xpack.core.ml.action.MlInfoAction.Response; +import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.function.Predicate; -public class MlInfoActionResponseTests extends AbstractStreamableTestCase { +public class MlInfoActionResponseTests extends + AbstractHlrcStreamableXContentTestCase { + + @Override + public MlInfoResponse doHlrcParseInstance(XContentParser parser) throws IOException { + return MlInfoResponse.fromXContent(parser); + } + + @Override + public Response convertHlrcToInternal(MlInfoResponse instance) { + return new Response(instance.getInfo()); + } + + @Override + protected Predicate getRandomFieldsExcludeFilter() { + return p -> true; + } @Override protected Response createTestInstance() {