Browse Source

HLRC: ML Get Calendar Events (#35747)

* HLRC: ML Get Calendar Events

* Addressing PR comments
Benjamin Trent 7 years ago
parent
commit
84db1e42c0

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

@@ -38,6 +38,7 @@ import org.elasticsearch.client.ml.DeleteModelSnapshotRequest;
 import org.elasticsearch.client.ml.FlushJobRequest;
 import org.elasticsearch.client.ml.ForecastJobRequest;
 import org.elasticsearch.client.ml.GetBucketsRequest;
+import org.elasticsearch.client.ml.GetCalendarEventsRequest;
 import org.elasticsearch.client.ml.GetCalendarsRequest;
 import org.elasticsearch.client.ml.GetCategoriesRequest;
 import org.elasticsearch.client.ml.GetDatafeedRequest;
@@ -539,6 +540,19 @@ final class MLRequestConverters {
         return request;
     }
 
+    static Request getCalendarEvents(GetCalendarEventsRequest getCalendarEventsRequest) throws IOException {
+        String endpoint = new EndpointBuilder()
+            .addPathPartAsIs("_xpack")
+            .addPathPartAsIs("ml")
+            .addPathPartAsIs("calendars")
+            .addPathPart(getCalendarEventsRequest.getCalendarId())
+            .addPathPartAsIs("events")
+            .build();
+        Request request = new Request(HttpGet.METHOD_NAME, endpoint);
+        request.setEntity(createEntity(getCalendarEventsRequest, REQUEST_BODY_CONTENT_TYPE));
+        return request;
+    }
+
     static Request postCalendarEvents(PostCalendarEventRequest postCalendarEventRequest) throws IOException {
         String endpoint = new EndpointBuilder()
             .addPathPartAsIs("_xpack")

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

@@ -36,6 +36,8 @@ import org.elasticsearch.client.ml.ForecastJobRequest;
 import org.elasticsearch.client.ml.ForecastJobResponse;
 import org.elasticsearch.client.ml.GetBucketsRequest;
 import org.elasticsearch.client.ml.GetBucketsResponse;
+import org.elasticsearch.client.ml.GetCalendarEventsRequest;
+import org.elasticsearch.client.ml.GetCalendarEventsResponse;
 import org.elasticsearch.client.ml.GetCalendarsRequest;
 import org.elasticsearch.client.ml.GetCalendarsResponse;
 import org.elasticsearch.client.ml.GetCategoriesRequest;
@@ -1386,6 +1388,47 @@ public final class MachineLearningClient {
                 Collections.emptySet());
     }
 
+    /**
+     * Gets the events for a machine learning calendar
+     * <p>
+     * For additional info
+     * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-calendar-event.html">
+     *  GET Calendar Events API</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 PostCalendarEventRequest} containing the scheduled events
+     * @throws IOException when there is a serialization issue sending the request or receiving the response
+     */
+    public GetCalendarEventsResponse getCalendarEvents(GetCalendarEventsRequest request, RequestOptions options) throws IOException {
+        return restHighLevelClient.performRequestAndParseEntity(request,
+            MLRequestConverters::getCalendarEvents,
+            options,
+            GetCalendarEventsResponse::fromXContent,
+            Collections.emptySet());
+    }
+
+    /**
+     * Gets the events for a a machine learning calendar asynchronously, notifies the listener on completion
+     * <p>
+     * For additional info
+     * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-calendar-event.html">
+     *  GET Calendar Events API</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 getCalendarEventsAsync(GetCalendarEventsRequest request, RequestOptions options,
+                                       ActionListener<GetCalendarEventsResponse> listener) {
+        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+            MLRequestConverters::getCalendarEvents,
+            options,
+            GetCalendarEventsResponse::fromXContent,
+            listener,
+            Collections.emptySet());
+    }
+
     /**
      * Creates new events for a a machine learning calendar
      * <p>

+ 169 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetCalendarEventsRequest.java

@@ -0,0 +1,169 @@
+/*
+ * 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 org.elasticsearch.client.ml.calendars.Calendar;
+import org.elasticsearch.client.ml.job.config.Job;
+import org.elasticsearch.client.ml.job.util.PageParams;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Get the Scheduled Events for a Calendar
+ */
+public class GetCalendarEventsRequest extends ActionRequest implements ToXContentObject {
+
+    public static final ParseField START = new ParseField("start");
+    public static final ParseField END = new ParseField("end");
+
+    public static final ConstructingObjectParser<GetCalendarEventsRequest, Void> PARSER =
+            new ConstructingObjectParser<>("get_calendar_events_request", a -> new GetCalendarEventsRequest((String)a[0]));
+
+    static {
+        PARSER.declareString(ConstructingObjectParser.constructorArg(), Calendar.ID);
+        PARSER.declareString(GetCalendarEventsRequest::setStart, START);
+        PARSER.declareString(GetCalendarEventsRequest::setEnd, END);
+        PARSER.declareString(GetCalendarEventsRequest::setJobId, Job.ID);
+        PARSER.declareObject(GetCalendarEventsRequest::setPageParams, PageParams.PARSER, PageParams.PAGE);
+    }
+
+    private final String calendarId;
+    private String start;
+    private String end;
+    private String jobId;
+    private PageParams pageParams;
+
+    /**
+     * Create a new request to get the ScheduledEvents for the given calendarId.
+     *
+     * @param calendarId The ID of the calendar.
+     *                   Can be `_all` to get ALL ScheduledEvents for all calendars.
+     */
+    public GetCalendarEventsRequest(String calendarId) {
+        this.calendarId = Objects.requireNonNull(calendarId, "[calendar_id] must not be null.");
+    }
+
+    public String getCalendarId() {
+        return calendarId;
+    }
+
+    public PageParams getPageParams() {
+        return pageParams;
+    }
+
+    /**
+     * The paging parameters for the gathered ScheduledEvents
+     * @param pageParams The desired paging params
+     */
+    public void setPageParams(PageParams pageParams) {
+        this.pageParams = pageParams;
+    }
+
+    public String getStart() {
+        return start;
+    }
+
+    /**
+     * Specifies to get events with timestamps after this time.
+     *
+     * @param start String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string
+     */
+    public void setStart(String start) {
+        this.start = start;
+    }
+
+    public String getEnd() {
+        return end;
+    }
+
+    /**
+     * Specifies to get events with timestamps earlier than this time.
+     *
+     * @param end String representation of a timestamp; may be an epoch seconds, epoch millis or an ISO string
+     */
+    public void setEnd(String end) {
+        this.end = end;
+    }
+
+    public String getJobId() {
+        return jobId;
+    }
+
+    /**
+     * The jobId for which to get the ScheduledEvents. When this option is used calendarId must be `_all`
+     * @param jobId The job for which to get the events.
+     */
+    public void setJobId(String jobId) {
+        this.jobId = jobId;
+    }
+
+    @Override
+    public ActionRequestValidationException validate() {
+        return null;
+    }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.field(Calendar.ID.getPreferredName(), calendarId);
+        if (start != null) {
+            builder.field(START.getPreferredName(), start);
+        }
+        if (end != null) {
+            builder.field(END.getPreferredName(), end);
+        }
+        if (jobId != null) {
+            builder.field(Job.ID.getPreferredName(), jobId);
+        }
+        if (pageParams != null) {
+            builder.field(PageParams.PAGE.getPreferredName(), pageParams);
+        }
+        builder.endObject();
+        return builder;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(calendarId, start, end, jobId, pageParams);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        GetCalendarEventsRequest other = (GetCalendarEventsRequest) obj;
+        return Objects.equals(calendarId, other.calendarId)
+            && Objects.equals(pageParams, other.pageParams)
+            && Objects.equals(start, other.start)
+            && Objects.equals(end, other.end)
+            && Objects.equals(jobId, other.jobId);
+    }
+}

+ 88 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/ml/GetCalendarEventsResponse.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.client.ml.calendars.ScheduledEvent;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
+
+/**
+ * Contains a {@link List} of the found {@link ScheduledEvent} objects and the total count found
+ */
+public class GetCalendarEventsResponse extends AbstractResultResponse<ScheduledEvent>  {
+
+    public static final ParseField RESULTS_FIELD = new ParseField("events");
+
+    @SuppressWarnings("unchecked")
+    public static final ConstructingObjectParser<GetCalendarEventsResponse, Void> PARSER =
+        new ConstructingObjectParser<>("calendar_events_response", true,
+            a -> new GetCalendarEventsResponse((List<ScheduledEvent>) a[0], (long) a[1]));
+
+    static {
+        PARSER.declareObjectArray(constructorArg(), ScheduledEvent.PARSER, RESULTS_FIELD);
+        PARSER.declareLong(constructorArg(), COUNT);
+    }
+
+    GetCalendarEventsResponse(List<ScheduledEvent> events, long count) {
+        super(RESULTS_FIELD, events, count);
+    }
+
+    /**
+     * The collection of {@link ScheduledEvent} objects found in the query
+     */
+    public List<ScheduledEvent> events() {
+        return results;
+    }
+
+    public static GetCalendarEventsResponse fromXContent(XContentParser parser) throws IOException {
+        return PARSER.parse(parser, null);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(results, count);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        GetCalendarEventsResponse other = (GetCalendarEventsResponse) obj;
+        return Objects.equals(results, other.results) && count == other.count;
+    }
+
+    @Override
+    public final String toString() {
+        return Strings.toString(this);
+    }
+}

+ 18 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/MLRequestConvertersTests.java

@@ -34,6 +34,7 @@ import org.elasticsearch.client.ml.DeleteModelSnapshotRequest;
 import org.elasticsearch.client.ml.FlushJobRequest;
 import org.elasticsearch.client.ml.ForecastJobRequest;
 import org.elasticsearch.client.ml.GetBucketsRequest;
+import org.elasticsearch.client.ml.GetCalendarEventsRequest;
 import org.elasticsearch.client.ml.GetCalendarsRequest;
 import org.elasticsearch.client.ml.GetCategoriesRequest;
 import org.elasticsearch.client.ml.GetDatafeedRequest;
@@ -591,6 +592,23 @@ public class MLRequestConvertersTests extends ESTestCase {
         assertEquals("/_xpack/ml/calendars/" + deleteCalendarRequest.getCalendarId(), request.getEndpoint());
     }
 
+    public void testGetCalendarEvents() throws IOException {
+        String calendarId = randomAlphaOfLength(10);
+        GetCalendarEventsRequest getCalendarEventsRequest = new GetCalendarEventsRequest(calendarId);
+        getCalendarEventsRequest.setStart("2018-08-08T00:00:00Z");
+        getCalendarEventsRequest.setEnd("2018-09-08T00:00:00Z");
+        getCalendarEventsRequest.setPageParams(new PageParams(100, 300));
+        getCalendarEventsRequest.setJobId(randomAlphaOfLength(10));
+
+        Request request = MLRequestConverters.getCalendarEvents(getCalendarEventsRequest);
+        assertEquals(HttpGet.METHOD_NAME, request.getMethod());
+        assertEquals("/_xpack/ml/calendars/" + calendarId + "/events", request.getEndpoint());
+        try (XContentParser parser = createParser(JsonXContent.jsonXContent, request.getEntity().getContent())) {
+            GetCalendarEventsRequest parsedRequest = GetCalendarEventsRequest.PARSER.apply(parser, null);
+            assertThat(parsedRequest, equalTo(getCalendarEventsRequest));
+        }
+    }
+
     public void testPostCalendarEvent() throws Exception {
         String calendarId = randomAlphaOfLength(10);
         List<ScheduledEvent> events = Arrays.asList(ScheduledEventTests.testInstance(),

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

@@ -41,6 +41,8 @@ import org.elasticsearch.client.ml.FlushJobRequest;
 import org.elasticsearch.client.ml.FlushJobResponse;
 import org.elasticsearch.client.ml.ForecastJobRequest;
 import org.elasticsearch.client.ml.ForecastJobResponse;
+import org.elasticsearch.client.ml.GetCalendarEventsRequest;
+import org.elasticsearch.client.ml.GetCalendarEventsResponse;
 import org.elasticsearch.client.ml.GetCalendarsRequest;
 import org.elasticsearch.client.ml.GetCalendarsResponse;
 import org.elasticsearch.client.ml.GetDatafeedRequest;
@@ -97,6 +99,7 @@ import org.elasticsearch.client.ml.job.config.JobState;
 import org.elasticsearch.client.ml.job.config.JobUpdate;
 import org.elasticsearch.client.ml.job.config.MlFilter;
 import org.elasticsearch.client.ml.job.stats.JobStats;
+import org.elasticsearch.client.ml.job.util.PageParams;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.rest.RestStatus;
@@ -921,6 +924,47 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
         assertThat(exception.status().getStatus(), equalTo(404));
     }
 
+    public void testGetCalendarEvent() throws Exception {
+        Calendar calendar = new Calendar("get-calendar-event-id", Collections.singletonList("get-calendar-event-job"), null);
+        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);
+
+        {
+            GetCalendarEventsRequest getCalendarEventsRequest = new GetCalendarEventsRequest(calendar.getId());
+
+            GetCalendarEventsResponse getCalendarEventsResponse = execute(getCalendarEventsRequest,
+                machineLearningClient::getCalendarEvents,
+                machineLearningClient::getCalendarEventsAsync);
+            assertThat(getCalendarEventsResponse.events().size(), equalTo(3));
+            assertThat(getCalendarEventsResponse.count(), equalTo(3L));
+        }
+        {
+            GetCalendarEventsRequest getCalendarEventsRequest = new GetCalendarEventsRequest(calendar.getId());
+            getCalendarEventsRequest.setPageParams(new PageParams(1, 2));
+            GetCalendarEventsResponse getCalendarEventsResponse = execute(getCalendarEventsRequest,
+                machineLearningClient::getCalendarEvents,
+                machineLearningClient::getCalendarEventsAsync);
+            assertThat(getCalendarEventsResponse.events().size(), equalTo(2));
+            assertThat(getCalendarEventsResponse.count(), equalTo(3L));
+        }
+        {
+            machineLearningClient.putJob(new PutJobRequest(buildJob("get-calendar-event-job")), RequestOptions.DEFAULT);
+            GetCalendarEventsRequest getCalendarEventsRequest = new GetCalendarEventsRequest("_all");
+            getCalendarEventsRequest.setJobId("get-calendar-event-job");
+            GetCalendarEventsResponse getCalendarEventsResponse = execute(getCalendarEventsRequest,
+                machineLearningClient::getCalendarEvents,
+                machineLearningClient::getCalendarEventsAsync);
+            assertThat(getCalendarEventsResponse.events().size(), equalTo(3));
+            assertThat(getCalendarEventsResponse.count(), equalTo(3L));
+        }
+    }
+
     public void testPostCalendarEvent() throws Exception {
         Calendar calendar = CalendarTests.testInstance();
         MachineLearningClient machineLearningClient = highLevelClient().machineLearning();

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

@@ -49,6 +49,8 @@ import org.elasticsearch.client.ml.ForecastJobRequest;
 import org.elasticsearch.client.ml.ForecastJobResponse;
 import org.elasticsearch.client.ml.GetBucketsRequest;
 import org.elasticsearch.client.ml.GetBucketsResponse;
+import org.elasticsearch.client.ml.GetCalendarEventsRequest;
+import org.elasticsearch.client.ml.GetCalendarEventsResponse;
 import org.elasticsearch.client.ml.GetCalendarsRequest;
 import org.elasticsearch.client.ml.GetCalendarsResponse;
 import org.elasticsearch.client.ml.GetCategoriesRequest;
@@ -2381,6 +2383,81 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
         assertTrue(latch.await(30L, TimeUnit.SECONDS));
     }
 
+    public void testGetCalendarEvent() throws IOException, InterruptedException {
+        RestHighLevelClient client = highLevelClient();
+
+        Calendar calendar = new Calendar("holidays", Collections.singletonList("job_1"), "A calendar for public holidays");
+        PutCalendarRequest putRequest = new PutCalendarRequest(calendar);
+        client.machineLearning().putCalendar(putRequest, RequestOptions.DEFAULT);
+        List<ScheduledEvent> events = Collections.singletonList(ScheduledEventTests.testInstance(calendar.getId(), null));
+        client.machineLearning().postCalendarEvent(new PostCalendarEventRequest("holidays", events), RequestOptions.DEFAULT);
+        {
+            // tag::get-calendar-events-request
+            GetCalendarEventsRequest request = new GetCalendarEventsRequest("holidays"); // <1>
+            // end::get-calendar-events-request
+
+            // tag::get-calendar-events-page
+            request.setPageParams(new PageParams(10, 20)); // <1>
+            // end::get-calendar-events-page
+
+            // tag::get-calendar-events-start
+            request.setStart("2018-08-01T00:00:00Z"); // <1>
+            // end::get-calendar-events-start
+
+            // tag::get-calendar-events-end
+            request.setEnd("2018-08-02T00:00:00Z"); // <1>
+            // end::get-calendar-events-end
+
+            // tag::get-calendar-events-jobid
+            request.setJobId("job_1"); // <1>
+            // end::get-calendar-events-jobid
+
+            // reset params
+            request.setPageParams(null);
+            request.setJobId(null);
+            request.setStart(null);
+            request.setEnd(null);
+
+            // tag::get-calendar-events-execute
+            GetCalendarEventsResponse response = client.machineLearning().getCalendarEvents(request, RequestOptions.DEFAULT);
+            // end::get-calendar-events-execute
+
+            // tag::get-calendar-events-response
+            long count = response.count(); // <1>
+            List<ScheduledEvent> scheduledEvents = response.events(); // <2>
+            // end::get-calendar-events-response
+            assertEquals(1, scheduledEvents.size());
+        }
+        {
+            GetCalendarEventsRequest request = new GetCalendarEventsRequest("holidays");
+
+            // tag::get-calendar-events-execute-listener
+            ActionListener<GetCalendarEventsResponse> listener =
+                new ActionListener<GetCalendarEventsResponse>() {
+                    @Override
+                    public void onResponse(GetCalendarEventsResponse getCalendarsResponse) {
+                        // <1>
+                    }
+
+                    @Override
+                    public void onFailure(Exception e) {
+                        // <2>
+                    }
+                };
+            // end::get-calendar-events-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-calendar-events-execute-async
+            client.machineLearning().getCalendarEventsAsync(request, RequestOptions.DEFAULT, listener); // <1>
+            // end::get-calendar-events-execute-async
+
+            assertTrue(latch.await(30L, TimeUnit.SECONDS));
+        }
+    }
+    
     public void testPostCalendarEvent() throws IOException, InterruptedException {
         RestHighLevelClient client = highLevelClient();
 

+ 55 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetCalendarEventsRequestTests.java

@@ -0,0 +1,55 @@
+/*
+ * 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.ml.job.util.PageParams;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractXContentTestCase;
+
+public class GetCalendarEventsRequestTests extends AbstractXContentTestCase<GetCalendarEventsRequest> {
+
+    @Override
+    protected GetCalendarEventsRequest createTestInstance() {
+        String calendarId = randomAlphaOfLengthBetween(1, 10);
+        GetCalendarEventsRequest request = new GetCalendarEventsRequest(calendarId);
+        if (randomBoolean()) {
+            request.setPageParams(new PageParams(1, 2));
+        }
+        if (randomBoolean()) {
+            request.setEnd(randomAlphaOfLength(10));
+        }
+        if (randomBoolean()) {
+            request.setStart(randomAlphaOfLength(10));
+        }
+        if (randomBoolean()) {
+            request.setJobId(randomAlphaOfLength(10));
+        }
+        return request;
+    }
+
+    @Override
+    protected GetCalendarEventsRequest doParseInstance(XContentParser parser) {
+        return GetCalendarEventsRequest.PARSER.apply(parser, null);
+    }
+
+    @Override
+    protected boolean supportsUnknownFields() {
+        return false;
+    }
+}

+ 53 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/ml/GetCalendarEventsResponseTests.java

@@ -0,0 +1,53 @@
+/*
+ * 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.ml.calendars.ScheduledEvent;
+import org.elasticsearch.client.ml.calendars.ScheduledEventTests;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractXContentTestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class GetCalendarEventsResponseTests extends AbstractXContentTestCase<GetCalendarEventsResponse> {
+
+    @Override
+    protected GetCalendarEventsResponse createTestInstance() {
+        String calendarId = randomAlphaOfLength(10);
+        List<ScheduledEvent> scheduledEvents = new ArrayList<>();
+        int count = randomIntBetween(0, 3);
+        for (int i=0; i<count; i++) {
+            scheduledEvents.add(ScheduledEventTests.testInstance(calendarId, randomAlphaOfLength(10)));
+        }
+        return new GetCalendarEventsResponse(scheduledEvents, count);
+    }
+
+    @Override
+    protected GetCalendarEventsResponse doParseInstance(XContentParser parser) throws IOException {
+        return GetCalendarEventsResponse.fromXContent(parser);
+    }
+
+    @Override
+    protected boolean supportsUnknownFields() {
+        return true;
+    }
+}

+ 65 - 0
docs/java-rest/high-level/ml/get-calendar-events.asciidoc

@@ -0,0 +1,65 @@
+--
+:api: get-calendar-events
+:request: GetCalendarEventsRequest
+:response: GetCalendarEventsResponse
+--
+[id="{upid}-{api}"]
+=== Get Calendar Events API
+Retrieves a calendars events.
+It accepts a +{request}+ and responds
+with a +{response}+ object.
+
+[id="{upid}-{api}-request"]
+==== Get Calendars Request
+
+A +{request}+ requires a non-null calendar ID.
+Using the literal `_all` returns the events for all calendars.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request]
+--------------------------------------------------
+<1> Constructing a new request for the specified calendarId
+
+==== Optional Arguments
+The following arguments are optional:
+
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-page]
+--------------------------------------------------
+<1> The page parameters `from` and `size`. `from` specifies the number of events to skip.
+`size` specifies the maximum number of events to get. Defaults to `0` and `100` respectively.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-start]
+--------------------------------------------------
+<1> Specifies to get events with timestamps after this time.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-end]
+--------------------------------------------------
+<1> Specifies to get events with timestamps earlier than this time.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-jobid]
+--------------------------------------------------
+<1> Get events for the job. When this option is used calendar_id must be `_all`
+
+include::../execution.asciidoc[]
+
+[id="{upid}-{api}-response"]
+==== Get calendars Response
+
+The returned +{response}+ contains the requested events:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-response]
+--------------------------------------------------
+<1> The count of events that were matched
+<2> The events retrieved

+ 2 - 0
docs/java-rest/high-level/supported-apis.asciidoc

@@ -266,6 +266,7 @@ The Java High Level REST Client supports the following Machine Learning APIs:
 * <<{upid}-get-categories>>
 * <<{upid}-get-calendars>>
 * <<{upid}-put-calendar>>
+* <<{upid}-get-calendar-events>>
 * <<{upid}-post-calendar-event>>
 * <<{upid}-put-calendar-job>>
 * <<{upid}-delete-calendar-job>>
@@ -304,6 +305,7 @@ include::ml/get-influencers.asciidoc[]
 include::ml/get-categories.asciidoc[]
 include::ml/get-calendars.asciidoc[]
 include::ml/put-calendar.asciidoc[]
+include::ml/get-calendar-events.asciidoc[]
 include::ml/post-calendar-event.asciidoc[]
 include::ml/put-calendar-job.asciidoc[]
 include::ml/delete-calendar-job.asciidoc[]