Skip to content

Commit bc8b249

Browse files
committed
HLRC ML Add Event To Calendar API (#35704)
* HLRC: ML Adding Post event to calendar api * Fixing tests and serialization * removing unused import
1 parent 7541536 commit bc8b249

13 files changed

+525
-4
lines changed

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

+16
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
5151
import org.elasticsearch.client.ml.GetRecordsRequest;
5252
import org.elasticsearch.client.ml.OpenJobRequest;
53+
import org.elasticsearch.client.ml.PostCalendarEventRequest;
5354
import org.elasticsearch.client.ml.PostDataRequest;
5455
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
5556
import org.elasticsearch.client.ml.PutCalendarJobRequest;
@@ -538,6 +539,21 @@ static Request deleteCalendar(DeleteCalendarRequest deleteCalendarRequest) {
538539
return request;
539540
}
540541

542+
static Request postCalendarEvents(PostCalendarEventRequest postCalendarEventRequest) throws IOException {
543+
String endpoint = new EndpointBuilder()
544+
.addPathPartAsIs("_xpack")
545+
.addPathPartAsIs("ml")
546+
.addPathPartAsIs("calendars")
547+
.addPathPart(postCalendarEventRequest.getCalendarId())
548+
.addPathPartAsIs("events")
549+
.build();
550+
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
551+
request.setEntity(createEntity(postCalendarEventRequest,
552+
REQUEST_BODY_CONTENT_TYPE,
553+
PostCalendarEventRequest.EXCLUDE_CALENDAR_ID_PARAMS));
554+
return request;
555+
}
556+
541557
static Request putFilter(PutFilterRequest putFilterRequest) throws IOException {
542558
String endpoint = new EndpointBuilder()
543559
.addPathPartAsIs("_xpack")

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

+43
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
import org.elasticsearch.client.ml.GetRecordsResponse;
6161
import org.elasticsearch.client.ml.OpenJobRequest;
6262
import org.elasticsearch.client.ml.OpenJobResponse;
63+
import org.elasticsearch.client.ml.PostCalendarEventRequest;
64+
import org.elasticsearch.client.ml.PostCalendarEventResponse;
6365
import org.elasticsearch.client.ml.PostDataRequest;
6466
import org.elasticsearch.client.ml.PostDataResponse;
6567
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
@@ -1384,6 +1386,47 @@ public void deleteCalendarAsync(DeleteCalendarRequest request, RequestOptions op
13841386
Collections.emptySet());
13851387
}
13861388

1389+
/**
1390+
* Creates new events for a a machine learning calendar
1391+
* <p>
1392+
* For additional info
1393+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-post-calendar-event.html">
1394+
* Add Events to Calendar API</a>
1395+
*
1396+
* @param request The request
1397+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1398+
* @return The {@link PostCalendarEventRequest} containing the scheduled events
1399+
* @throws IOException when there is a serialization issue sending the request or receiving the response
1400+
*/
1401+
public PostCalendarEventResponse postCalendarEvent(PostCalendarEventRequest request, RequestOptions options) throws IOException {
1402+
return restHighLevelClient.performRequestAndParseEntity(request,
1403+
MLRequestConverters::postCalendarEvents,
1404+
options,
1405+
PostCalendarEventResponse::fromXContent,
1406+
Collections.emptySet());
1407+
}
1408+
1409+
/**
1410+
* Creates new events for a a machine learning calendar asynchronously, notifies the listener on completion
1411+
* <p>
1412+
* For additional info
1413+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-post-calendar-event.html">
1414+
* Add Events to Calendar API</a>
1415+
*
1416+
* @param request The request
1417+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1418+
* @param listener Listener to be notified upon request completion
1419+
*/
1420+
public void postCalendarEventAsync(PostCalendarEventRequest request, RequestOptions options,
1421+
ActionListener<PostCalendarEventResponse> listener) {
1422+
restHighLevelClient.performRequestAsyncAndParseEntity(request,
1423+
MLRequestConverters::postCalendarEvents,
1424+
options,
1425+
PostCalendarEventResponse::fromXContent,
1426+
listener,
1427+
Collections.emptySet());
1428+
}
1429+
13871430
/**
13881431
* Creates a new Machine Learning Filter
13891432
* <p>

client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,12 @@ static Request deleteScript(DeleteStoredScriptRequest deleteStoredScriptRequest)
661661
}
662662

663663
static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
664-
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
664+
return createEntity(toXContent, xContentType, ToXContent.EMPTY_PARAMS);
665+
}
666+
667+
static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType, ToXContent.Params toXContentParams)
668+
throws IOException {
669+
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, toXContentParams, false).toBytesRef();
665670
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));
666671
}
667672

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.ml;
20+
21+
import org.elasticsearch.action.ActionRequest;
22+
import org.elasticsearch.action.ActionRequestValidationException;
23+
import org.elasticsearch.client.ml.calendars.Calendar;
24+
import org.elasticsearch.client.ml.calendars.ScheduledEvent;
25+
import org.elasticsearch.common.ParseField;
26+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
27+
import org.elasticsearch.common.xcontent.ToXContentObject;
28+
import org.elasticsearch.common.xcontent.XContentBuilder;
29+
30+
import java.io.IOException;
31+
import java.util.Collections;
32+
import java.util.List;
33+
import java.util.Objects;
34+
35+
/**
36+
* Request to add a ScheduledEvent to a Machine Learning calendar
37+
*/
38+
public class PostCalendarEventRequest extends ActionRequest implements ToXContentObject {
39+
40+
private final String calendarId;
41+
private final List<ScheduledEvent> scheduledEvents;
42+
43+
public static final String INCLUDE_CALENDAR_ID_KEY = "include_calendar_id";
44+
public static final ParseField EVENTS = new ParseField("events");
45+
46+
@SuppressWarnings("unchecked")
47+
public static final ConstructingObjectParser<PostCalendarEventRequest, Void> PARSER =
48+
new ConstructingObjectParser<>("post_calendar_event_request",
49+
a -> new PostCalendarEventRequest((String)a[0], (List<ScheduledEvent>)a[1]));
50+
51+
static {
52+
PARSER.declareString(ConstructingObjectParser.constructorArg(), Calendar.ID);
53+
PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(),
54+
(p, c) -> ScheduledEvent.PARSER.apply(p, null), EVENTS);
55+
}
56+
public static final MapParams EXCLUDE_CALENDAR_ID_PARAMS =
57+
new MapParams(Collections.singletonMap(INCLUDE_CALENDAR_ID_KEY, Boolean.toString(false)));
58+
59+
/**
60+
* Create a new PostCalendarEventRequest with an existing non-null calendarId and a list of Scheduled events
61+
*
62+
* @param calendarId The ID of the calendar, must be non-null
63+
* @param scheduledEvents The non-null, non-empty, list of {@link ScheduledEvent} objects to add to the calendar
64+
*/
65+
public PostCalendarEventRequest(String calendarId, List<ScheduledEvent> scheduledEvents) {
66+
this.calendarId = Objects.requireNonNull(calendarId, "[calendar_id] must not be null.");
67+
this.scheduledEvents = Objects.requireNonNull(scheduledEvents, "[events] must not be null.");
68+
if (scheduledEvents.isEmpty()) {
69+
throw new IllegalArgumentException("At least 1 event is required");
70+
}
71+
}
72+
73+
public String getCalendarId() {
74+
return calendarId;
75+
}
76+
77+
public List<ScheduledEvent> getScheduledEvents() {
78+
return scheduledEvents;
79+
}
80+
81+
@Override
82+
public ActionRequestValidationException validate() {
83+
return null;
84+
}
85+
86+
@Override
87+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
88+
builder.startObject();
89+
if (params.paramAsBoolean(INCLUDE_CALENDAR_ID_KEY, true)) {
90+
builder.field(Calendar.ID.getPreferredName(), calendarId);
91+
}
92+
builder.field(EVENTS.getPreferredName(), scheduledEvents);
93+
builder.endObject();
94+
return builder;
95+
}
96+
97+
@Override
98+
public int hashCode() {
99+
return Objects.hash(calendarId, scheduledEvents);
100+
}
101+
102+
@Override
103+
public boolean equals(Object obj) {
104+
if (this == obj) {
105+
return true;
106+
}
107+
if (obj == null || getClass() != obj.getClass()) {
108+
return false;
109+
}
110+
PostCalendarEventRequest other = (PostCalendarEventRequest) obj;
111+
return Objects.equals(calendarId, other.calendarId) && Objects.equals(scheduledEvents, other.scheduledEvents);
112+
}
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.ml;
20+
21+
import org.elasticsearch.action.ActionResponse;
22+
import org.elasticsearch.client.ml.calendars.ScheduledEvent;
23+
import org.elasticsearch.common.ParseField;
24+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
25+
import org.elasticsearch.common.xcontent.ToXContentObject;
26+
import org.elasticsearch.common.xcontent.XContentBuilder;
27+
import org.elasticsearch.common.xcontent.XContentParser;
28+
29+
import java.io.IOException;
30+
import java.util.List;
31+
import java.util.Objects;
32+
33+
/**
34+
* Response to adding ScheduledEvent(s) to a Machine Learning calendar
35+
*/
36+
public class PostCalendarEventResponse extends ActionResponse implements ToXContentObject {
37+
38+
private final List<ScheduledEvent> scheduledEvents;
39+
public static final ParseField EVENTS = new ParseField("events");
40+
41+
@SuppressWarnings("unchecked")
42+
public static final ConstructingObjectParser<PostCalendarEventResponse, Void> PARSER =
43+
new ConstructingObjectParser<>("post_calendar_event_response",
44+
true,
45+
a -> new PostCalendarEventResponse((List<ScheduledEvent>)a[0]));
46+
47+
static {
48+
PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(),
49+
(p, c) -> ScheduledEvent.PARSER.apply(p, null), EVENTS);
50+
}
51+
52+
public static PostCalendarEventResponse fromXContent(XContentParser parser) throws IOException {
53+
return PARSER.parse(parser, null);
54+
}
55+
56+
/**
57+
* Create a new PostCalendarEventResponse containing the scheduled Events
58+
*
59+
* @param scheduledEvents The list of {@link ScheduledEvent} objects
60+
*/
61+
public PostCalendarEventResponse(List<ScheduledEvent> scheduledEvents) {
62+
this.scheduledEvents = scheduledEvents;
63+
}
64+
65+
public List<ScheduledEvent> getScheduledEvents() {
66+
return scheduledEvents;
67+
}
68+
69+
@Override
70+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
71+
builder.startObject();
72+
builder.field(EVENTS.getPreferredName(), scheduledEvents);
73+
builder.endObject();
74+
return builder;
75+
}
76+
77+
@Override
78+
public int hashCode(){
79+
return Objects.hash(scheduledEvents);
80+
}
81+
82+
@Override
83+
public boolean equals(Object obj) {
84+
if (this == obj) {
85+
return true;
86+
}
87+
if (obj == null || getClass() != obj.getClass()) {
88+
return false;
89+
}
90+
PostCalendarEventResponse other = (PostCalendarEventResponse) obj;
91+
return Objects.equals(scheduledEvents, other.scheduledEvents);
92+
}
93+
}

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

+21
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.elasticsearch.client.ml.GetOverallBucketsRequest;
4747
import org.elasticsearch.client.ml.GetRecordsRequest;
4848
import org.elasticsearch.client.ml.OpenJobRequest;
49+
import org.elasticsearch.client.ml.PostCalendarEventRequest;
4950
import org.elasticsearch.client.ml.PostDataRequest;
5051
import org.elasticsearch.client.ml.PreviewDatafeedRequest;
5152
import org.elasticsearch.client.ml.PutCalendarJobRequest;
@@ -61,6 +62,8 @@
6162
import org.elasticsearch.client.ml.UpdateModelSnapshotRequest;
6263
import org.elasticsearch.client.ml.calendars.Calendar;
6364
import org.elasticsearch.client.ml.calendars.CalendarTests;
65+
import org.elasticsearch.client.ml.calendars.ScheduledEvent;
66+
import org.elasticsearch.client.ml.calendars.ScheduledEventTests;
6467
import org.elasticsearch.client.ml.datafeed.DatafeedConfig;
6568
import org.elasticsearch.client.ml.datafeed.DatafeedConfigTests;
6669
import org.elasticsearch.client.ml.job.config.AnalysisConfig;
@@ -73,6 +76,7 @@
7376
import org.elasticsearch.client.ml.job.util.PageParams;
7477
import org.elasticsearch.common.Strings;
7578
import org.elasticsearch.common.unit.TimeValue;
79+
import org.elasticsearch.common.xcontent.XContentBuilder;
7680
import org.elasticsearch.common.xcontent.XContentParser;
7781
import org.elasticsearch.common.xcontent.XContentType;
7882
import org.elasticsearch.common.xcontent.json.JsonXContent;
@@ -83,6 +87,7 @@
8387
import java.util.Arrays;
8488
import java.util.Collections;
8589
import java.util.HashMap;
90+
import java.util.List;
8691
import java.util.Map;
8792

8893
import static org.hamcrest.Matchers.equalTo;
@@ -586,6 +591,22 @@ public void testDeleteCalendar() {
586591
assertEquals("/_xpack/ml/calendars/" + deleteCalendarRequest.getCalendarId(), request.getEndpoint());
587592
}
588593

594+
public void testPostCalendarEvent() throws Exception {
595+
String calendarId = randomAlphaOfLength(10);
596+
List<ScheduledEvent> events = Arrays.asList(ScheduledEventTests.testInstance(),
597+
ScheduledEventTests.testInstance(),
598+
ScheduledEventTests.testInstance());
599+
PostCalendarEventRequest postCalendarEventRequest = new PostCalendarEventRequest(calendarId, events);
600+
601+
Request request = MLRequestConverters.postCalendarEvents(postCalendarEventRequest);
602+
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
603+
assertEquals("/_xpack/ml/calendars/" + calendarId + "/events", request.getEndpoint());
604+
605+
XContentBuilder builder = JsonXContent.contentBuilder();
606+
builder = postCalendarEventRequest.toXContent(builder, PostCalendarEventRequest.EXCLUDE_CALENDAR_ID_PARAMS);
607+
assertEquals(Strings.toString(builder), requestEntityToString(request));
608+
}
609+
589610
public void testPutFilter() throws IOException {
590611
MlFilter filter = MlFilterTests.createRandomBuilder("foo").build();
591612
PutFilterRequest putFilterRequest = new PutFilterRequest(filter);

0 commit comments

Comments
 (0)