From 90a8e4b259f4d7590cf90594742cfc65e2c45e57 Mon Sep 17 00:00:00 2001 From: Benjamin Trent <ben.w.trent@gmail.com> Date: Wed, 21 Nov 2018 16:22:04 -0600 Subject: [PATCH] HLRC: ML Delete event from Calendar (#35760) * HLRC: Delete event from calendar * adjusting tests * adjusting code to make it more readable --- .../client/MLRequestConverters.java | 13 ++++ .../client/MachineLearningClient.java | 43 ++++++++++ .../client/ml/DeleteCalendarEventRequest.java | 78 +++++++++++++++++++ .../client/MLRequestConvertersTests.java | 10 +++ .../client/MachineLearningIT.java | 39 ++++++++++ .../MlClientDocumentationIT.java | 67 +++++++++++++++- .../ml/DeleteCalendarEventRequestTests.java | 37 +++++++++ .../ml/delete-calendar-event.asciidoc | 36 +++++++++ .../ml/delete-calendar-job.asciidoc | 2 +- .../high-level/supported-apis.asciidoc | 2 + 10 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarEventRequest.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarEventRequestTests.java create mode 100644 docs/java-rest/high-level/ml/delete-calendar-event.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 15c2b1617d2..57c40852078 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.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.DeleteCalendarEventRequest; import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; @@ -584,6 +585,18 @@ final class MLRequestConverters { return request; } + static Request deleteCalendarEvent(DeleteCalendarEventRequest deleteCalendarEventRequest) { + String endpoint = new EndpointBuilder() + .addPathPartAsIs("_xpack") + .addPathPartAsIs("ml") + .addPathPartAsIs("calendars") + .addPathPart(deleteCalendarEventRequest.getCalendarId()) + .addPathPartAsIs("events") + .addPathPart(deleteCalendarEventRequest.getEventId()) + .build(); + return new Request(HttpDelete.METHOD_NAME, endpoint); + } + static Request putFilter(PutFilterRequest putFilterRequest) throws IOException { 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 bba67a792da..204dfaa87bc 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.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.DeleteCalendarEventRequest; import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; @@ -1513,6 +1514,48 @@ public final class MachineLearningClient { Collections.emptySet()); } + /** + * Removes a Scheduled Event from a calendar + * <p> + * For additional info + * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-event.html"> + * ML Delete calendar event 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 AcknowledgedResponse deleteCalendarEvent(DeleteCalendarEventRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + MLRequestConverters::deleteCalendarEvent, + options, + AcknowledgedResponse::fromXContent, + Collections.emptySet()); + } + + /** + * Removes a Scheduled Event 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-event.html"> + * ML Delete calendar event 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 deleteCalendarEventAsync(DeleteCalendarEventRequest request, + RequestOptions options, + ActionListener<AcknowledgedResponse> listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + MLRequestConverters::deleteCalendarEvent, + options, + AcknowledgedResponse::fromXContent, + listener, + Collections.emptySet()); + } + /** * Creates a new Machine Learning Filter * <p> diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarEventRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarEventRequest.java new file mode 100644 index 00000000000..b10d3cc7344 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/DeleteCalendarEventRequest.java @@ -0,0 +1,78 @@ +/* + * 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.util.Objects; + +/** + * Request class for removing an event from an existing calendar + */ +public class DeleteCalendarEventRequest extends ActionRequest { + + private final String eventId; + private final String calendarId; + + /** + * Create a new request referencing an existing Calendar and which event to remove + * from it. + * + * @param calendarId The non-null ID of the calendar + * @param eventId Scheduled Event to remove from the calendar, Cannot be null. + */ + public DeleteCalendarEventRequest(String calendarId, String eventId) { + this.calendarId = Objects.requireNonNull(calendarId, "[calendar_id] must not be null."); + this.eventId = Objects.requireNonNull(eventId, "[event_id] must not be null."); + } + + public String getEventId() { + return eventId; + } + + public String getCalendarId() { + return calendarId; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public int hashCode() { + return Objects.hash(eventId, calendarId); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + DeleteCalendarEventRequest that = (DeleteCalendarEventRequest) other; + return Objects.equals(eventId, that.eventId) && + 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 8bbad5a059a..f5c421c329f 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.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.DeleteCalendarEventRequest; import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; @@ -644,6 +645,15 @@ public class MLRequestConvertersTests extends ESTestCase { assertEquals(Strings.toString(builder), requestEntityToString(request)); } + public void testDeleteCalendarEvent() { + String calendarId = randomAlphaOfLength(10); + String eventId = randomAlphaOfLength(5); + DeleteCalendarEventRequest deleteCalendarEventRequest = new DeleteCalendarEventRequest(calendarId, eventId); + Request request = MLRequestConverters.deleteCalendarEvent(deleteCalendarEventRequest); + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/ml/calendars/" + calendarId + "/events/" + eventId, request.getEndpoint()); + } + public void testPutFilter() throws IOException { MlFilter filter = MlFilterTests.createRandomBuilder("foo").build(); PutFilterRequest putFilterRequest = new PutFilterRequest(filter); 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 0f4f44b15a0..fe8d5d96b59 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.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.DeleteCalendarEventRequest; import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; @@ -119,7 +120,9 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasSize; @@ -986,6 +989,42 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase { assertThat(postCalendarEventResponse.getScheduledEvents(), containsInAnyOrder(events.toArray())); } + public void testDeleteCalendarEvent() throws IOException { + Calendar calendar = CalendarTests.testInstance(); + MachineLearningClient machineLearningClient = highLevelClient().machineLearning(); + machineLearningClient.putCalendar(new PutCalendarRequest(calendar), RequestOptions.DEFAULT); + + List<ScheduledEvent> events = new ArrayList<>(3); + for (int i = 0; i < 3; i++) { + events.add(ScheduledEventTests.testInstance(calendar.getId(), null)); + } + + machineLearningClient.postCalendarEvent(new PostCalendarEventRequest(calendar.getId(), events), RequestOptions.DEFAULT); + GetCalendarEventsResponse getCalendarEventsResponse = + machineLearningClient.getCalendarEvents(new GetCalendarEventsRequest(calendar.getId()), RequestOptions.DEFAULT); + + assertThat(getCalendarEventsResponse.events().size(), equalTo(3)); + String deletedEvent = getCalendarEventsResponse.events().get(0).getEventId(); + + DeleteCalendarEventRequest deleteCalendarEventRequest = new DeleteCalendarEventRequest(calendar.getId(), deletedEvent); + + AcknowledgedResponse response = execute(deleteCalendarEventRequest, + machineLearningClient::deleteCalendarEvent, + machineLearningClient::deleteCalendarEventAsync); + + assertThat(response.isAcknowledged(), is(true)); + + getCalendarEventsResponse = + machineLearningClient.getCalendarEvents(new GetCalendarEventsRequest(calendar.getId()), RequestOptions.DEFAULT); + List<String> remainingIds = getCalendarEventsResponse.events() + .stream() + .map(ScheduledEvent::getEventId) + .collect(Collectors.toList()); + + assertThat(remainingIds.size(), equalTo(2)); + assertThat(remainingIds, not(hasItem(deletedEvent))); + } + public void testPutFilter() throws Exception { String filterId = "filter-job-test"; MlFilter mlFilter = MlFilter.builder(filterId) 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 ee3a9eadebf..17f5bcffbcc 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.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.ml.CloseJobRequest; import org.elasticsearch.client.ml.CloseJobResponse; +import org.elasticsearch.client.ml.DeleteCalendarEventRequest; import org.elasticsearch.client.ml.DeleteCalendarJobRequest; import org.elasticsearch.client.ml.DeleteCalendarRequest; import org.elasticsearch.client.ml.DeleteDatafeedRequest; @@ -2590,7 +2591,71 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } } - + + public void testDeleteCalendarEvent() 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); + List<ScheduledEvent> events = Arrays.asList(ScheduledEventTests.testInstance(calendar.getId(), null), + ScheduledEventTests.testInstance(calendar.getId(), null)); + client.machineLearning().postCalendarEvent(new PostCalendarEventRequest("holidays", events), RequestOptions.DEFAULT); + GetCalendarEventsResponse getCalendarEventsResponse = + client.machineLearning().getCalendarEvents(new GetCalendarEventsRequest("holidays"), RequestOptions.DEFAULT); + { + + // tag::delete-calendar-event-request + DeleteCalendarEventRequest request = new DeleteCalendarEventRequest("holidays", // <1> + "EventId"); // <2> + // end::delete-calendar-event-request + + request = new DeleteCalendarEventRequest("holidays", getCalendarEventsResponse.events().get(0).getEventId()); + + // tag::delete-calendar-event-execute + AcknowledgedResponse response = client.machineLearning().deleteCalendarEvent(request, RequestOptions.DEFAULT); + // end::delete-calendar-event-execute + + // tag::delete-calendar-event-response + boolean acknowledged = response.isAcknowledged(); // <1> + // end::delete-calendar-event-response + + assertThat(acknowledged, is(true)); + } + { + DeleteCalendarEventRequest request = new DeleteCalendarEventRequest("holidays", + getCalendarEventsResponse.events().get(1).getEventId()); + + // tag::delete-calendar-event-execute-listener + ActionListener<AcknowledgedResponse> listener = + new ActionListener<AcknowledgedResponse>() { + @Override + public void onResponse(AcknowledgedResponse deleteCalendarEventResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::delete-calendar-event-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-event-execute-async + client.machineLearning().deleteCalendarEventAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::delete-calendar-event-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + + public void testCreateFilter() throws Exception { RestHighLevelClient client = highLevelClient(); { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarEventRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarEventRequestTests.java new file mode 100644 index 00000000000..186ef807245 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ml/DeleteCalendarEventRequestTests.java @@ -0,0 +1,37 @@ +/* + * 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 DeleteCalendarEventRequestTests extends ESTestCase { + + public void testWithNullId() { + NullPointerException ex = expectThrows(NullPointerException.class, + () -> new DeleteCalendarEventRequest(null, "event1")); + assertEquals("[calendar_id] must not be null.", ex.getMessage()); + } + + public void testWithNullEvent() { + NullPointerException ex = expectThrows(NullPointerException.class, + () ->new DeleteCalendarEventRequest("calendarId", null)); + assertEquals("[event_id] must not be null.", ex.getMessage()); + } +} diff --git a/docs/java-rest/high-level/ml/delete-calendar-event.asciidoc b/docs/java-rest/high-level/ml/delete-calendar-event.asciidoc new file mode 100644 index 00000000000..dcd09a0581d --- /dev/null +++ b/docs/java-rest/high-level/ml/delete-calendar-event.asciidoc @@ -0,0 +1,36 @@ +-- +:api: delete-calendar-event +:request: DeleteCalendarEventRequest +:response: AcknowledgedResponse +-- +[id="{upid}-{api}"] +=== Delete Calendar Event API +Removes a scheduled event from an existing {ml} calendar. +The API accepts a +{request}+ and responds +with a +{response}+ object. + +[id="{upid}-{api}-request"] +==== Delete Calendar Event Request + +A +{request}+ is constructed referencing a non-null +calendar ID, and eventId 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 eventId to remove from the calendar + +[id="{upid}-{api}-response"] +==== Delete Calendar Event Response + +The returned +{response}+ acknowledges the success of the request: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> Acknowledgement of the request and its success + +include::../execution.asciidoc[] diff --git a/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc b/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc index d7686315f0f..4e55a221b85 100644 --- a/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc +++ b/docs/java-rest/high-level/ml/delete-calendar-job.asciidoc @@ -23,7 +23,7 @@ include-tagged::{doc-tests-file}[{api}-request] <2> The JobIds to remove from the calendar [id="{upid}-{api}-response"] -==== Delete Calendar Response +==== Delete Calendar Job Response The returned +{response}+ contains the updated Calendar: diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index e23967bfe30..02158673da2 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -268,6 +268,7 @@ The Java High Level REST Client supports the following Machine Learning APIs: * <<{upid}-put-calendar>> * <<{upid}-get-calendar-events>> * <<{upid}-post-calendar-event>> +* <<{upid}-delete-calendar-event>> * <<{upid}-put-calendar-job>> * <<{upid}-delete-calendar-job>> * <<{upid}-delete-calendar>> @@ -308,6 +309,7 @@ include::ml/get-calendars.asciidoc[] include::ml/put-calendar.asciidoc[] include::ml/get-calendar-events.asciidoc[] include::ml/post-calendar-event.asciidoc[] +include::ml/delete-calendar-event.asciidoc[] include::ml/put-calendar-job.asciidoc[] include::ml/delete-calendar-job.asciidoc[] include::ml/delete-calendar.asciidoc[]