Browse Source

HLRC: ML Delete job from calendar (#35713)

Benjamin Trent 7 years ago
parent
commit
d707838c02

+ 13 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/MLRequestConverters.java

@@ -28,6 +28,7 @@ import org.apache.http.entity.ByteArrayEntity;
 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 @@ final class MLRequestConverters {
         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")

+ 42 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java

@@ -22,6 +22,7 @@ import org.elasticsearch.action.ActionListener;
 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 final class MachineLearningClient {
             Collections.emptySet());
     }
 
+    /**
+     * Removes Machine Learning Job(s) from a calendar
+     * <p>
+     * For additional info
+     * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-job.html">
+     * ML Delete calendar job documentation</a>
+     *
+     * @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
+     * <p>
+     * For additional info
+     * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-job.html">
+     * ML Delete calendar job documentation</a>
+     *
+     * @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<PutCalendarResponse> listener) {
+        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+            MLRequestConverters::deleteCalendarJob,
+            options,
+            PutCalendarResponse::fromXContent,
+            listener,
+            Collections.emptySet());
+    }
 
     /**
      * Deletes the given Machine Learning Calendar

+ 88 - 0
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<String> 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<String> 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);
+    }
+}

+ 12 - 1
client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java

@@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpGet;
 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 class MLRequestConvertersTests extends ESTestCase {
         }
     }
 
-    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 class MLRequestConvertersTests extends ESTestCase {
         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";

+ 24 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java

@@ -29,6 +29,7 @@ import org.elasticsearch.action.support.WriteRequest;
 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 class MachineLearningIT extends ESRestHighLevelClientTestCase {
         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();

+ 55 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java

@@ -35,6 +35,7 @@ import org.elasticsearch.client.RequestOptions;
 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 class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
         }
     }
 
+    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<PutCalendarResponse> listener =
+                new ActionListener<PutCalendarResponse>() {
+                    @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();
 

+ 43 - 0
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());
+    }
+}

+ 36 - 0
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[]

+ 2 - 0
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[]