Skip to content

Commit d707838

Browse files
authored
HLRC: ML Delete job from calendar (#35713)
1 parent 8892408 commit d707838

File tree

9 files changed

+315
-1
lines changed

9 files changed

+315
-1
lines changed

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

+13
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.lucene.util.BytesRef;
2929
import org.elasticsearch.client.RequestConverters.EndpointBuilder;
3030
import org.elasticsearch.client.ml.CloseJobRequest;
31+
import org.elasticsearch.client.ml.DeleteCalendarJobRequest;
3132
import org.elasticsearch.client.ml.DeleteCalendarRequest;
3233
import org.elasticsearch.client.ml.DeleteDatafeedRequest;
3334
import org.elasticsearch.client.ml.DeleteFilterRequest;
@@ -514,6 +515,18 @@ static Request putCalendarJob(PutCalendarJobRequest putCalendarJobRequest) {
514515
return new Request(HttpPut.METHOD_NAME, endpoint);
515516
}
516517

518+
static Request deleteCalendarJob(DeleteCalendarJobRequest deleteCalendarJobRequest) {
519+
String endpoint = new EndpointBuilder()
520+
.addPathPartAsIs("_xpack")
521+
.addPathPartAsIs("ml")
522+
.addPathPartAsIs("calendars")
523+
.addPathPart(deleteCalendarJobRequest.getCalendarId())
524+
.addPathPartAsIs("jobs")
525+
.addPathPart(Strings.collectionToCommaDelimitedString(deleteCalendarJobRequest.getJobIds()))
526+
.build();
527+
return new Request(HttpDelete.METHOD_NAME, endpoint);
528+
}
529+
517530
static Request deleteCalendar(DeleteCalendarRequest deleteCalendarRequest) {
518531
String endpoint = new EndpointBuilder()
519532
.addPathPartAsIs("_xpack")

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

+42
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.action.support.master.AcknowledgedResponse;
2323
import org.elasticsearch.client.ml.CloseJobRequest;
2424
import org.elasticsearch.client.ml.CloseJobResponse;
25+
import org.elasticsearch.client.ml.DeleteCalendarJobRequest;
2526
import org.elasticsearch.client.ml.DeleteCalendarRequest;
2627
import org.elasticsearch.client.ml.DeleteDatafeedRequest;
2728
import org.elasticsearch.client.ml.DeleteFilterRequest;
@@ -1301,6 +1302,47 @@ public void putCalendarJobAsync(PutCalendarJobRequest request, RequestOptions op
13011302
Collections.emptySet());
13021303
}
13031304

1305+
/**
1306+
* Removes Machine Learning Job(s) from a calendar
1307+
* <p>
1308+
* For additional info
1309+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-job.html">
1310+
* ML Delete calendar job documentation</a>
1311+
*
1312+
* @param request The request
1313+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1314+
* @return The {@link PutCalendarResponse} containing the updated calendar
1315+
* @throws IOException when there is a serialization issue sending the request or receiving the response
1316+
*/
1317+
public PutCalendarResponse deleteCalendarJob(DeleteCalendarJobRequest request, RequestOptions options) throws IOException {
1318+
return restHighLevelClient.performRequestAndParseEntity(request,
1319+
MLRequestConverters::deleteCalendarJob,
1320+
options,
1321+
PutCalendarResponse::fromXContent,
1322+
Collections.emptySet());
1323+
}
1324+
1325+
/**
1326+
* Removes Machine Learning Job(s) from a calendar, notifies listener when completed
1327+
* <p>
1328+
* For additional info
1329+
* see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-calendar-job.html">
1330+
* ML Delete calendar job documentation</a>
1331+
*
1332+
* @param request The request
1333+
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
1334+
* @param listener Listener to be notified upon request completion
1335+
*/
1336+
public void deleteCalendarJobAsync(DeleteCalendarJobRequest request,
1337+
RequestOptions options,
1338+
ActionListener<PutCalendarResponse> listener) {
1339+
restHighLevelClient.performRequestAsyncAndParseEntity(request,
1340+
MLRequestConverters::deleteCalendarJob,
1341+
options,
1342+
PutCalendarResponse::fromXContent,
1343+
listener,
1344+
Collections.emptySet());
1345+
}
13041346

13051347
/**
13061348
* Deletes the given Machine Learning Calendar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
24+
import java.security.InvalidParameterException;
25+
import java.util.Arrays;
26+
import java.util.List;
27+
import java.util.Objects;
28+
29+
/**
30+
* Request class for removing Machine Learning Jobs from an existing calendar
31+
*/
32+
public class DeleteCalendarJobRequest extends ActionRequest {
33+
34+
private final List<String> jobIds;
35+
private final String calendarId;
36+
37+
/**
38+
* Create a new request referencing an existing Calendar and which JobIds to remove
39+
* from it.
40+
*
41+
* @param calendarId The non-null ID of the calendar
42+
* @param jobIds JobIds to remove from the calendar, cannot be empty, or contain null values.
43+
* It can be a list of jobs or groups.
44+
*/
45+
public DeleteCalendarJobRequest(String calendarId, String... jobIds) {
46+
this.calendarId = Objects.requireNonNull(calendarId, "[calendar_id] must not be null.");
47+
if (jobIds.length == 0) {
48+
throw new InvalidParameterException("jobIds must not be empty.");
49+
}
50+
if (Arrays.stream(jobIds).anyMatch(Objects::isNull)) {
51+
throw new NullPointerException("jobIds must not contain null values.");
52+
}
53+
this.jobIds = Arrays.asList(jobIds);
54+
}
55+
56+
public List<String> getJobIds() {
57+
return jobIds;
58+
}
59+
60+
public String getCalendarId() {
61+
return calendarId;
62+
}
63+
64+
@Override
65+
public ActionRequestValidationException validate() {
66+
return null;
67+
}
68+
69+
@Override
70+
public int hashCode() {
71+
return Objects.hash(jobIds, calendarId);
72+
}
73+
74+
@Override
75+
public boolean equals(Object other) {
76+
if (this == other) {
77+
return true;
78+
}
79+
80+
if (other == null || getClass() != other.getClass()) {
81+
return false;
82+
}
83+
84+
DeleteCalendarJobRequest that = (DeleteCalendarJobRequest) other;
85+
return Objects.equals(jobIds, that.jobIds) &&
86+
Objects.equals(calendarId, that.calendarId);
87+
}
88+
}

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.http.client.methods.HttpPost;
2525
import org.apache.http.client.methods.HttpPut;
2626
import org.elasticsearch.client.ml.CloseJobRequest;
27+
import org.elasticsearch.client.ml.DeleteCalendarJobRequest;
2728
import org.elasticsearch.client.ml.DeleteCalendarRequest;
2829
import org.elasticsearch.client.ml.DeleteDatafeedRequest;
2930
import org.elasticsearch.client.ml.DeleteFilterRequest;
@@ -536,7 +537,7 @@ public void testPutCalendar() throws IOException {
536537
}
537538
}
538539

539-
public void testPutCalendarJob() throws IOException {
540+
public void testPutCalendarJob() {
540541
String calendarId = randomAlphaOfLength(10);
541542
String job1 = randomAlphaOfLength(5);
542543
String job2 = randomAlphaOfLength(5);
@@ -546,6 +547,16 @@ public void testPutCalendarJob() throws IOException {
546547
assertEquals("/_xpack/ml/calendars/" + calendarId + "/jobs/" + job1 + "," + job2, request.getEndpoint());
547548
}
548549

550+
public void testDeleteCalendarJob() {
551+
String calendarId = randomAlphaOfLength(10);
552+
String job1 = randomAlphaOfLength(5);
553+
String job2 = randomAlphaOfLength(5);
554+
DeleteCalendarJobRequest deleteCalendarJobRequest = new DeleteCalendarJobRequest(calendarId, job1, job2);
555+
Request request = MLRequestConverters.deleteCalendarJob(deleteCalendarJobRequest);
556+
assertEquals(HttpDelete.METHOD_NAME, request.getMethod());
557+
assertEquals("/_xpack/ml/calendars/" + calendarId + "/jobs/" + job1 + "," + job2, request.getEndpoint());
558+
}
559+
549560
public void testGetCalendars() throws IOException {
550561
GetCalendarsRequest getCalendarsRequest = new GetCalendarsRequest();
551562
String expectedEndpoint = "/_xpack/ml/calendars";

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

+24
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.action.support.master.AcknowledgedResponse;
3030
import org.elasticsearch.client.ml.CloseJobRequest;
3131
import org.elasticsearch.client.ml.CloseJobResponse;
32+
import org.elasticsearch.client.ml.DeleteCalendarJobRequest;
3233
import org.elasticsearch.client.ml.DeleteCalendarRequest;
3334
import org.elasticsearch.client.ml.DeleteDatafeedRequest;
3435
import org.elasticsearch.client.ml.DeleteFilterRequest;
@@ -851,6 +852,29 @@ public void testPutCalendarJob() throws IOException {
851852
assertThat(putCalendarResponse.getCalendar().getJobIds(), containsInAnyOrder(jobId1, jobId2, "put-calendar-job-0"));
852853
}
853854

855+
public void testDeleteCalendarJob() throws IOException {
856+
Calendar calendar = new Calendar("del-calendar-job-id",
857+
Arrays.asList("del-calendar-job-0", "del-calendar-job-1", "del-calendar-job-2"),
858+
null);
859+
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
860+
PutCalendarResponse putCalendarResponse =
861+
machineLearningClient.putCalendar(new PutCalendarRequest(calendar), RequestOptions.DEFAULT);
862+
863+
assertThat(putCalendarResponse.getCalendar().getJobIds(),
864+
containsInAnyOrder("del-calendar-job-0", "del-calendar-job-1", "del-calendar-job-2"));
865+
866+
String jobId1 = "del-calendar-job-0";
867+
String jobId2 = "del-calendar-job-2";
868+
869+
DeleteCalendarJobRequest deleteCalendarJobRequest = new DeleteCalendarJobRequest(calendar.getId(), jobId1, jobId2);
870+
871+
putCalendarResponse = execute(deleteCalendarJobRequest,
872+
machineLearningClient::deleteCalendarJob,
873+
machineLearningClient::deleteCalendarJobAsync);
874+
875+
assertThat(putCalendarResponse.getCalendar().getJobIds(), containsInAnyOrder("del-calendar-job-1"));
876+
}
877+
854878
public void testGetCalendars() throws Exception {
855879
Calendar calendar1 = CalendarTests.testInstance();
856880
Calendar calendar2 = CalendarTests.testInstance();

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

+55
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.client.RestHighLevelClient;
3636
import org.elasticsearch.client.ml.CloseJobRequest;
3737
import org.elasticsearch.client.ml.CloseJobResponse;
38+
import org.elasticsearch.client.ml.DeleteCalendarJobRequest;
3839
import org.elasticsearch.client.ml.DeleteCalendarRequest;
3940
import org.elasticsearch.client.ml.DeleteDatafeedRequest;
4041
import org.elasticsearch.client.ml.DeleteFilterRequest;
@@ -2214,6 +2215,60 @@ public void onFailure(Exception e) {
22142215
}
22152216
}
22162217

2218+
public void testDeleteCalendarJob() throws IOException, InterruptedException {
2219+
RestHighLevelClient client = highLevelClient();
2220+
2221+
Calendar calendar = new Calendar("holidays",
2222+
Arrays.asList("job_1", "job_group_1", "job_2"),
2223+
"A calendar for public holidays");
2224+
PutCalendarRequest putRequest = new PutCalendarRequest(calendar);
2225+
client.machineLearning().putCalendar(putRequest, RequestOptions.DEFAULT);
2226+
{
2227+
// tag::delete-calendar-job-request
2228+
DeleteCalendarJobRequest request = new DeleteCalendarJobRequest("holidays", // <1>
2229+
"job_1", "job_group_1"); // <2>
2230+
// end::delete-calendar-job-request
2231+
2232+
// tag::delete-calendar-job-execute
2233+
PutCalendarResponse response = client.machineLearning().deleteCalendarJob(request, RequestOptions.DEFAULT);
2234+
// end::delete-calendar-job-execute
2235+
2236+
// tag::delete-calendar-job-response
2237+
Calendar updatedCalendar = response.getCalendar(); // <1>
2238+
// end::delete-calendar-job-response
2239+
2240+
assertThat(updatedCalendar.getJobIds(), containsInAnyOrder("job_2"));
2241+
}
2242+
{
2243+
DeleteCalendarJobRequest request = new DeleteCalendarJobRequest("holidays", "job_2");
2244+
2245+
// tag::delete-calendar-job-execute-listener
2246+
ActionListener<PutCalendarResponse> listener =
2247+
new ActionListener<PutCalendarResponse>() {
2248+
@Override
2249+
public void onResponse(PutCalendarResponse deleteCalendarsResponse) {
2250+
// <1>
2251+
}
2252+
2253+
@Override
2254+
public void onFailure(Exception e) {
2255+
// <2>
2256+
}
2257+
};
2258+
// end::delete-calendar-job-execute-listener
2259+
2260+
// Replace the empty listener by a blocking listener in test
2261+
final CountDownLatch latch = new CountDownLatch(1);
2262+
listener = new LatchedActionListener<>(listener, latch);
2263+
2264+
// tag::delete-calendar-job-execute-async
2265+
client.machineLearning().deleteCalendarJobAsync(request, RequestOptions.DEFAULT, listener); // <1>
2266+
// end::delete-calendar-job-execute-async
2267+
2268+
assertTrue(latch.await(30L, TimeUnit.SECONDS));
2269+
}
2270+
}
2271+
22172272
public void testGetCalendar() throws IOException, InterruptedException {
22182273
RestHighLevelClient client = highLevelClient();
22192274

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
20+
package org.elasticsearch.client.ml;
21+
22+
import org.elasticsearch.test.ESTestCase;
23+
24+
public class DeleteCalendarJobRequestTests extends ESTestCase {
25+
26+
public void testWithNullId() {
27+
NullPointerException ex = expectThrows(NullPointerException.class,
28+
() -> new DeleteCalendarJobRequest(null, "job1"));
29+
assertEquals("[calendar_id] must not be null.", ex.getMessage());
30+
}
31+
32+
public void testSetJobIds() {
33+
String calendarId = randomAlphaOfLength(10);
34+
35+
NullPointerException ex = expectThrows(NullPointerException.class,
36+
() ->new DeleteCalendarJobRequest(calendarId, "job1", null));
37+
assertEquals("jobIds must not contain null values.", ex.getMessage());
38+
39+
IllegalArgumentException illegalArgumentException =
40+
expectThrows(IllegalArgumentException.class, () -> new DeleteCalendarJobRequest(calendarId));
41+
assertEquals("jobIds must not be empty.", illegalArgumentException.getMessage());
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--
2+
:api: delete-calendar-job
3+
:request: DeleteCalendarJobRequest
4+
:response: PutCalendarResponse
5+
--
6+
[id="{upid}-{api}"]
7+
=== Delete Calendar Job API
8+
Removes {ml} jobs from an existing {ml} calendar.
9+
The API accepts a +{request}+ and responds
10+
with a +{response}+ object.
11+
12+
[id="{upid}-{api}-request"]
13+
==== Delete Calendar Job Request
14+
15+
A +{request}+ is constructed referencing a non-null
16+
calendar ID, and JobIDs which to remove from the calendar
17+
18+
["source","java",subs="attributes,callouts,macros"]
19+
--------------------------------------------------
20+
include-tagged::{doc-tests-file}[{api}-request]
21+
--------------------------------------------------
22+
<1> The ID of the calendar from which to remove the jobs
23+
<2> The JobIds to remove from the calendar
24+
25+
[id="{upid}-{api}-response"]
26+
==== Delete Calendar Response
27+
28+
The returned +{response}+ contains the updated Calendar:
29+
30+
["source","java",subs="attributes,callouts,macros"]
31+
--------------------------------------------------
32+
include-tagged::{doc-tests-file}[{api}-response]
33+
--------------------------------------------------
34+
<1> The updated Calendar with the jobs removed
35+
36+
include::../execution.asciidoc[]

0 commit comments

Comments
 (0)