From d707838c026d3ffe0113d780da9badd40d33a2b3 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Tue, 20 Nov 2018 07:43:34 -0600 Subject: [PATCH] HLRC: ML Delete job from calendar (#35713) --- .../client/MLRequestConverters.java | 13 +++ .../client/MachineLearningClient.java | 42 +++++++++ .../client/ml/DeleteCalendarJobRequest.java | 88 +++++++++++++++++++ .../client/MLRequestConvertersTests.java | 13 ++- .../client/MachineLearningIT.java | 24 +++++ .../MlClientDocumentationIT.java | 55 ++++++++++++ .../ml/DeleteCalendarJobRequestTests.java | 43 +++++++++ .../ml/delete-calendar-job.asciidoc | 36 ++++++++ .../high-level/supported-apis.asciidoc | 2 + 9 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarJobRequest.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarJobRequestTests.java create mode 100644 docs/java-rest/high-level/ml/delete-calendar-job.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 62c1740ab8250..6f2f40728592f 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 @@ -28,6 +28,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.client.RequestConverters.EndpointBuilder; import org.elasticsearch.client.ml.CloseJobRequest; +import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; import org.elasticsearch.client.ml.DeleteFilterRequest; @@ -514,6 +515,18 @@ static Request putCalendarJob(PutCalendarJobRequest putCalendarJobRequest) { return new Request(HttpPut.METHOD_NAME, endpoint); } + static Request deleteCalendarJob(DeleteCalendarJobRequest deleteCalendarJobRequest) { + String endpoint = new EndpointBuilder() + .addPathPartAsIs("_xpack") + .addPathPartAsIs("ml") + .addPathPartAsIs("calendars") + .addPathPart(deleteCalendarJobRequest.getCalendarId()) + .addPathPartAsIs("jobs") + .addPathPart(Strings.collectionToCommaDelimitedString(deleteCalendarJobRequest.getJobIds())) + .build(); + return new Request(HttpDelete.METHOD_NAME, endpoint); + } + static Request deleteCalendar(DeleteCalendarRequest deleteCalendarRequest) { 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 c34385bc7599e..bb526b810c4dd 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 @@ -22,6 +22,7 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobResponse; +import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; import org.elasticsearch.client.ml.DeleteFilterRequest; @@ -1301,6 +1302,47 @@ public void putCalendarJobAsync(PutCalendarJobRequest request, RequestOptions op Collections.emptySet()); } + /** + * Removes Machine Learning Job(s) from a calendar + *

+ * For additional info + * see + * ML Delete calendar job documentation + * + * @param request The request + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return The {@link PutCalendarResponse} containing the updated calendar + * @throws IOException when there is a serialization issue sending the request or receiving the response + */ + public PutCalendarResponse deleteCalendarJob(DeleteCalendarJobRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + MLRequestConverters::deleteCalendarJob, + options, + PutCalendarResponse::fromXContent, + Collections.emptySet()); + } + + /** + * Removes Machine Learning Job(s) from a calendar, notifies listener when completed + *

+ * For additional info + * see + * ML Delete calendar job documentation + * + * @param request The request + * @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 deleteCalendarJobAsync(DeleteCalendarJobRequest request, + RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + MLRequestConverters::deleteCalendarJob, + options, + PutCalendarResponse::fromXContent, + listener, + Collections.emptySet()); + } /** * Deletes the given Machine Learning Calendar diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarJobRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarJobRequest.java new file mode 100644 index 0000000000000..a1bd3a412c722 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarJobRequest.java @@ -0,0 +1,88 @@ +/* + * 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.action.ActionRequest; +import org.elasticsearch.action.ActionRequestValidationException; + +import java.security.InvalidParameterException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Request class for removing Machine Learning Jobs from an existing calendar + */ +public class DeleteCalendarJobRequest extends ActionRequest { + + private final List jobIds; + private final String calendarId; + + /** + * Create a new request referencing an existing Calendar and which JobIds to remove + * from it. + * + * @param calendarId The non-null ID of the calendar + * @param jobIds JobIds to remove from the calendar, cannot be empty, or contain null values. + * It can be a list of jobs or groups. + */ + public DeleteCalendarJobRequest(String calendarId, String... jobIds) { + this.calendarId = Objects.requireNonNull(calendarId, "[calendar_id] must not be null."); + if (jobIds.length == 0) { + throw new InvalidParameterException("jobIds must not be empty."); + } + if (Arrays.stream(jobIds).anyMatch(Objects::isNull)) { + throw new NullPointerException("jobIds must not contain null values."); + } + this.jobIds = Arrays.asList(jobIds); + } + + public List getJobIds() { + return jobIds; + } + + public String getCalendarId() { + return calendarId; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public int hashCode() { + return Objects.hash(jobIds, calendarId); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + DeleteCalendarJobRequest that = (DeleteCalendarJobRequest) other; + return Objects.equals(jobIds, that.jobIds) && + Objects.equals(calendarId, that.calendarId); + } +} 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 b47ee4d4bbb12..7e47f8c1ea8d3 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 @@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.ml.CloseJobRequest; +import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; import org.elasticsearch.client.ml.DeleteFilterRequest; @@ -536,7 +537,7 @@ public void testPutCalendar() throws IOException { } } - public void testPutCalendarJob() throws IOException { + public void testPutCalendarJob() { String calendarId = randomAlphaOfLength(10); String job1 = randomAlphaOfLength(5); String job2 = randomAlphaOfLength(5); @@ -546,6 +547,16 @@ public void testPutCalendarJob() throws IOException { assertEquals("/_xpack/ml/calendars/" + calendarId + "/jobs/" + job1 + "," + job2, request.getEndpoint()); } + public void testDeleteCalendarJob() { + String calendarId = randomAlphaOfLength(10); + String job1 = randomAlphaOfLength(5); + String job2 = randomAlphaOfLength(5); + DeleteCalendarJobRequest deleteCalendarJobRequest = new DeleteCalendarJobRequest(calendarId, job1, job2); + Request request = MLRequestConverters.deleteCalendarJob(deleteCalendarJobRequest); + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/ml/calendars/" + calendarId + "/jobs/" + job1 + "," + job2, request.getEndpoint()); + } + public void testGetCalendars() throws IOException { GetCalendarsRequest getCalendarsRequest = new GetCalendarsRequest(); String expectedEndpoint = "/_xpack/ml/calendars"; 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 4b52c8d8d5419..7716d7c20bb03 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 @@ -29,6 +29,7 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobResponse; +import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; import org.elasticsearch.client.ml.DeleteFilterRequest; @@ -851,6 +852,29 @@ public void testPutCalendarJob() throws IOException { assertThat(putCalendarResponse.getCalendar().getJobIds(), containsInAnyOrder(jobId1, jobId2, "put-calendar-job-0")); } + public void testDeleteCalendarJob() throws IOException { + Calendar calendar = new Calendar("del-calendar-job-id", + Arrays.asList("del-calendar-job-0", "del-calendar-job-1", "del-calendar-job-2"), + null); + MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); + PutCalendarResponse putCalendarResponse = + machineLearningClient.putCalendar(new PutCalendarRequest(calendar), RequestOptions.DEFAULT); + + assertThat(putCalendarResponse.getCalendar().getJobIds(), + containsInAnyOrder("del-calendar-job-0", "del-calendar-job-1", "del-calendar-job-2")); + + String jobId1 = "del-calendar-job-0"; + String jobId2 = "del-calendar-job-2"; + + DeleteCalendarJobRequest deleteCalendarJobRequest = new DeleteCalendarJobRequest(calendar.getId(), jobId1, jobId2); + + putCalendarResponse = execute(deleteCalendarJobRequest, + machineLearningClient::deleteCalendarJob, + machineLearningClient::deleteCalendarJobAsync); + + assertThat(putCalendarResponse.getCalendar().getJobIds(), containsInAnyOrder("del-calendar-job-1")); + } + public void testGetCalendars() throws Exception { Calendar calendar1 = CalendarTests.testInstance(); Calendar calendar2 = CalendarTests.testInstance(); 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 38203a38d8144..4886bce185338 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 @@ -35,6 +35,7 @@ import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobResponse; +import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; import org.elasticsearch.client.ml.DeleteFilterRequest; @@ -2214,6 +2215,60 @@ public void onFailure(Exception e) { } } + public void testDeleteCalendarJob() throws IOException, InterruptedException { + RestHighLevelClient client = highLevelClient(); + + Calendar calendar = new Calendar("holidays", + Arrays.asList("job_1", "job_group_1", "job_2"), + "A calendar for public holidays"); + PutCalendarRequest putRequest = new PutCalendarRequest(calendar); + client.machineLearning().putCalendar(putRequest, RequestOptions.DEFAULT); + { + // tag::delete-calendar-job-request + DeleteCalendarJobRequest request = new DeleteCalendarJobRequest("holidays", // <1> + "job_1", "job_group_1"); // <2> + // end::delete-calendar-job-request + + // tag::delete-calendar-job-execute + PutCalendarResponse response = client.machineLearning().deleteCalendarJob(request, RequestOptions.DEFAULT); + // end::delete-calendar-job-execute + + // tag::delete-calendar-job-response + Calendar updatedCalendar = response.getCalendar(); // <1> + // end::delete-calendar-job-response + + assertThat(updatedCalendar.getJobIds(), containsInAnyOrder("job_2")); + } + { + DeleteCalendarJobRequest request = new DeleteCalendarJobRequest("holidays", "job_2"); + + // tag::delete-calendar-job-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(PutCalendarResponse deleteCalendarsResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::delete-calendar-job-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::delete-calendar-job-execute-async + client.machineLearning().deleteCalendarJobAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::delete-calendar-job-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testGetCalendar() throws IOException, InterruptedException { RestHighLevelClient client = highLevelClient(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarJobRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarJobRequestTests.java new file mode 100644 index 0000000000000..63d78b45986b5 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarJobRequestTests.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.ml; + +import org.elasticsearch.test.ESTestCase; + +public class DeleteCalendarJobRequestTests extends ESTestCase { + + public void testWithNullId() { + NullPointerException ex = expectThrows(NullPointerException.class, + () -> new DeleteCalendarJobRequest(null, "job1")); + assertEquals("[calendar_id] must not be null.", ex.getMessage()); + } + + public void testSetJobIds() { + String calendarId = randomAlphaOfLength(10); + + NullPointerException ex = expectThrows(NullPointerException.class, + () ->new DeleteCalendarJobRequest(calendarId, "job1", null)); + assertEquals("jobIds must not contain null values.", ex.getMessage()); + + IllegalArgumentException illegalArgumentException = + expectThrows(IllegalArgumentException.class, () -> new DeleteCalendarJobRequest(calendarId)); + assertEquals("jobIds must not be empty.", illegalArgumentException.getMessage()); + } +} diff --git a/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc b/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc new file mode 100644 index 0000000000000..d7686315f0f2f --- /dev/null +++ b/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc @@ -0,0 +1,36 @@ +-- +:api: delete-calendar-job +:request: DeleteCalendarJobRequest +:response: PutCalendarResponse +-- +[id="{upid}-{api}"] +=== Delete Calendar Job API +Removes {ml} jobs from an existing {ml} calendar. +The API accepts a +{request}+ and responds +with a +{response}+ object. + +[id="{upid}-{api}-request"] +==== Delete Calendar Job Request + +A +{request}+ is constructed referencing a non-null +calendar ID, and JobIDs which to remove from the calendar + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> The ID of the calendar from which to remove the jobs +<2> The JobIds to remove from the calendar + +[id="{upid}-{api}-response"] +==== Delete Calendar Response + +The returned +{response}+ contains the updated Calendar: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> The updated Calendar with the jobs removed + +include::../execution.asciidoc[] diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 76f05570174b6..3a1878ab78b2b 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -267,6 +267,7 @@ The Java High Level REST Client supports the following Machine Learning APIs: * <<{upid}-get-calendars>> * <<{upid}-put-calendar>> * <<{upid}-put-calendar-job>> +* <<{upid}-delete-calendar-job>> * <<{upid}-delete-calendar>> * <<{upid}-put-filter>> * <<{upid}-get-filters>> @@ -303,6 +304,7 @@ include::ml/get-categories.asciidoc[] include::ml/get-calendars.asciidoc[] include::ml/put-calendar.asciidoc[] include::ml/put-calendar-job.asciidoc[] +include::ml/delete-calendar-job.asciidoc[] include::ml/delete-calendar.asciidoc[] include::ml/put-filter.asciidoc[] include::ml/get-model-snapshots.asciidoc[]