Skip to content

Commit

Permalink
HLRC: ML Delete job from calendar (#35713)
Browse files Browse the repository at this point in the history
  • Loading branch information
benwtrent authored Nov 20, 2018
1 parent 8892408 commit d707838
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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;
Expand Down Expand Up @@ -514,6 +515,18 @@ static Request putCalendarJob(PutCalendarJobRequest putCalendarJobRequest) {
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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -1301,6 +1302,47 @@ public void putCalendarJobAsync(PutCalendarJobRequest request, RequestOptions op
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;
Expand Down Expand Up @@ -536,7 +537,7 @@ public void testPutCalendar() throws IOException {
}
}

public void testPutCalendarJob() throws IOException {
public void testPutCalendarJob() {
String calendarId = randomAlphaOfLength(10);
String job1 = randomAlphaOfLength(5);
String job2 = randomAlphaOfLength(5);
Expand All @@ -546,6 +547,16 @@ public void testPutCalendarJob() throws IOException {
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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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;
Expand Down Expand Up @@ -851,6 +852,29 @@ public void testPutCalendarJob() throws IOException {
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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;
Expand Down Expand Up @@ -2214,6 +2215,60 @@ public void onFailure(Exception e) {
}
}

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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions docs/java-rest/high-level/ml/delete-calendar-job.asciidoc
Original file line number Diff line number Diff line change
@@ -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[]
Loading

0 comments on commit d707838

Please sign in to comment.