[ML] Delete calendar events endpoint (elastic/x-pack-elasticsearch#3388)

* Delete calendar events endpoint

Original commit: elastic/x-pack-elasticsearch@70aebfae2c
This commit is contained in:
David Kyle 2018-01-09 11:55:36 +00:00 committed by GitHub
parent bd3d652901
commit f73a7803ce
13 changed files with 514 additions and 39 deletions

View File

@ -45,7 +45,6 @@ public class DeleteCalendarAction extends Action<DeleteCalendarAction.Request, D
private String calendarId;
Request() {
}
public Request(String calendarId) {
@ -117,5 +116,4 @@ public class DeleteCalendarAction extends Action<DeleteCalendarAction.Request, D
writeAcknowledged(out);
}
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ml.action;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.ml.calendars.Calendar;
import org.elasticsearch.xpack.ml.calendars.ScheduledEvent;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
import java.io.IOException;
import java.util.Objects;
public class DeleteCalendarEventAction extends Action<DeleteCalendarEventAction.Request, DeleteCalendarEventAction.Response,
DeleteCalendarEventAction.RequestBuilder> {
public static final DeleteCalendarEventAction INSTANCE = new DeleteCalendarEventAction();
public static final String NAME = "cluster:admin/xpack/ml/calendars/events/delete";
private DeleteCalendarEventAction() {
super(NAME);
}
@Override
public RequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new RequestBuilder(client, this);
}
@Override
public Response newResponse() {
return new Response();
}
public static class Request extends AcknowledgedRequest<Request> {
private String calendarId;
private String eventId;
Request() {
}
public Request(String calendarId, String eventId) {
this.calendarId = ExceptionsHelper.requireNonNull(calendarId, Calendar.ID.getPreferredName());
this.eventId = ExceptionsHelper.requireNonNull(eventId, ScheduledEvent.EVENT_ID.getPreferredName());
}
public String getCalendarId() {
return calendarId;
}
public String getEventId() {
return eventId;
}
@Override
public ActionRequestValidationException validate() {
return null;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
calendarId = in.readString();
eventId = in.readString();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(calendarId);
out.writeString(eventId);
}
@Override
public int hashCode() {
return Objects.hash(eventId, calendarId);
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Request other = (Request) obj;
return Objects.equals(eventId, other.eventId) && Objects.equals(calendarId, other.calendarId);
}
}
public static class RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder> {
public RequestBuilder(ElasticsearchClient client, DeleteCalendarEventAction action) {
super(client, action, new Request());
}
}
public static class Response extends AcknowledgedResponse {
public Response(boolean acknowledged) {
super(acknowledged);
}
private Response() {}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
readAcknowledged(in);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
writeAcknowledged(out);
}
}
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.ml.calendars;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
@ -39,6 +40,7 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
public static final ParseField START_TIME = new ParseField("start_time");
public static final ParseField END_TIME = new ParseField("end_time");
public static final ParseField TYPE = new ParseField("type");
public static final ParseField EVENT_ID = new ParseField("event_id");
public static final ParseField RESULTS_FIELD = new ParseField("events");
@ -81,12 +83,14 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
private final ZonedDateTime startTime;
private final ZonedDateTime endTime;
private final String calendarId;
private final String eventId;
ScheduledEvent(String description, ZonedDateTime startTime, ZonedDateTime endTime, String calendarId) {
ScheduledEvent(String description, ZonedDateTime startTime, ZonedDateTime endTime, String calendarId, @Nullable String eventId) {
this.description = Objects.requireNonNull(description);
this.startTime = Objects.requireNonNull(startTime);
this.endTime = Objects.requireNonNull(endTime);
this.calendarId = Objects.requireNonNull(calendarId);
this.eventId = eventId;
}
public ScheduledEvent(StreamInput in) throws IOException {
@ -94,6 +98,7 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
startTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.readVLong()), ZoneOffset.UTC);
endTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.readVLong()), ZoneOffset.UTC);
calendarId = in.readString();
eventId = in.readOptionalString();
}
public String getDescription() {
@ -112,6 +117,10 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
return calendarId;
}
public String getEventId() {
return eventId;
}
/**
* Convert the scheduled event to a detection rule.
* The rule will have 2 time based conditions for the start and
@ -146,6 +155,7 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
out.writeVLong(startTime.toInstant().toEpochMilli());
out.writeVLong(endTime.toInstant().toEpochMilli());
out.writeString(calendarId);
out.writeOptionalString(eventId);
}
@Override
@ -155,6 +165,9 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
builder.dateField(START_TIME.getPreferredName(), START_TIME.getPreferredName() + "_string", startTime.toInstant().toEpochMilli());
builder.dateField(END_TIME.getPreferredName(), END_TIME.getPreferredName() + "_string", endTime.toInstant().toEpochMilli());
builder.field(Calendar.ID.getPreferredName(), calendarId);
if (eventId != null) {
builder.field(EVENT_ID.getPreferredName(), eventId);
}
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
builder.field(TYPE.getPreferredName(), SCHEDULED_EVENT_TYPE);
}
@ -197,7 +210,7 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
private ZonedDateTime startTime;
private ZonedDateTime endTime;
private String calendarId;
private String eventId;
public Builder description(String description) {
this.description = description;
@ -223,6 +236,11 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
return calendarId;
}
public Builder eventId(String eventId) {
this.eventId = eventId;
return this;
}
public ScheduledEvent build() {
if (description == null) {
throw ExceptionsHelper.badRequestException(
@ -249,7 +267,9 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
"] must come before end time [" + endTime + "]");
}
return new ScheduledEvent(description, startTime, endTime, calendarId);
ScheduledEvent event = new ScheduledEvent(description, startTime, endTime, calendarId, eventId);
return event;
}
}
}

View File

@ -1062,14 +1062,16 @@ public class JobProvider {
List<ScheduledEvent> events = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
events.add(parseSearchHit(hit, ScheduledEvent.PARSER, handler::onFailure).build());
ScheduledEvent.Builder event = parseSearchHit(hit, ScheduledEvent.PARSER, handler::onFailure);
event.eventId(hit.getId());
events.add(event.build());
}
handler.onResponse(new QueryPage<>(events, response.getHits().getTotalHits(),
ScheduledEvent.RESULTS_FIELD));
},
handler::onFailure)
, client::search);
handler::onFailure),
client::search);
}
public void getForecastRequestStats(String jobId, String forecastId, Consumer<ForecastRequestStats> handler,

View File

@ -24,8 +24,8 @@ import java.util.List;
public class ScheduledEventsQueryBuilder {
public static final int DEFAULT_SIZE = 1000;
private int from = 0;
private int size = DEFAULT_SIZE;
private Integer from = 0;
private Integer size = DEFAULT_SIZE;
private List<String> calendarIds;
private String after;
@ -46,12 +46,22 @@ public class ScheduledEventsQueryBuilder {
return this;
}
public ScheduledEventsQueryBuilder from(int from) {
/**
* Set the query from parameter.
* @param from If null then no from will be set
* @return this
*/
public ScheduledEventsQueryBuilder from(Integer from) {
this.from = from;
return this;
}
public ScheduledEventsQueryBuilder size(int size) {
/**
* Set the query size parameter.
* @param size If null then no size will be set
* @return this
*/
public ScheduledEventsQueryBuilder size(Integer size) {
this.size = size;
return this;
}
@ -78,8 +88,12 @@ public class ScheduledEventsQueryBuilder {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.sort(ScheduledEvent.START_TIME.getPreferredName());
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
if (from != null) {
searchSourceBuilder.from(from);
}
if (size != null) {
searchSourceBuilder.size(size);
}
if (queries.isEmpty()) {
searchSourceBuilder.query(typeQuery);

View File

@ -52,6 +52,11 @@ import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.ml.action.CloseJobAction;
import org.elasticsearch.xpack.ml.action.DeleteCalendarAction;
import org.elasticsearch.xpack.ml.action.DeleteCalendarEventAction;
import org.elasticsearch.xpack.ml.action.GetCalendarEventsAction;
import org.elasticsearch.xpack.ml.action.PostCalendarEventsAction;
import org.elasticsearch.xpack.ml.action.TransportDeleteCalendarEventAction;
import org.elasticsearch.xpack.ml.action.UpdateCalendarJobAction;
import org.elasticsearch.xpack.ml.action.DeleteDatafeedAction;
import org.elasticsearch.xpack.ml.action.DeleteExpiredDataAction;
import org.elasticsearch.xpack.ml.action.DeleteFilterAction;
@ -61,7 +66,6 @@ import org.elasticsearch.xpack.ml.action.FinalizeJobExecutionAction;
import org.elasticsearch.xpack.ml.action.FlushJobAction;
import org.elasticsearch.xpack.ml.action.ForecastJobAction;
import org.elasticsearch.xpack.ml.action.GetBucketsAction;
import org.elasticsearch.xpack.ml.action.GetCalendarEventsAction;
import org.elasticsearch.xpack.ml.action.GetCalendarsAction;
import org.elasticsearch.xpack.ml.action.GetCategoriesAction;
import org.elasticsearch.xpack.ml.action.GetDatafeedsAction;
@ -76,7 +80,6 @@ import org.elasticsearch.xpack.ml.action.GetRecordsAction;
import org.elasticsearch.xpack.ml.action.IsolateDatafeedAction;
import org.elasticsearch.xpack.ml.action.KillProcessAction;
import org.elasticsearch.xpack.ml.action.OpenJobAction;
import org.elasticsearch.xpack.ml.action.PostCalendarEventsAction;
import org.elasticsearch.xpack.ml.action.PostDataAction;
import org.elasticsearch.xpack.ml.action.PreviewDatafeedAction;
import org.elasticsearch.xpack.ml.action.PutCalendarAction;
@ -129,7 +132,6 @@ import org.elasticsearch.xpack.ml.action.TransportUpdateModelSnapshotAction;
import org.elasticsearch.xpack.ml.action.TransportUpdateProcessAction;
import org.elasticsearch.xpack.ml.action.TransportValidateDetectorAction;
import org.elasticsearch.xpack.ml.action.TransportValidateJobConfigAction;
import org.elasticsearch.xpack.ml.action.UpdateCalendarJobAction;
import org.elasticsearch.xpack.ml.action.UpdateDatafeedAction;
import org.elasticsearch.xpack.ml.action.UpdateJobAction;
import org.elasticsearch.xpack.ml.action.UpdateModelSnapshotAction;
@ -163,6 +165,7 @@ import org.elasticsearch.xpack.ml.notifications.AuditMessage;
import org.elasticsearch.xpack.ml.notifications.Auditor;
import org.elasticsearch.xpack.ml.rest.RestDeleteExpiredDataAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestDeleteCalendarAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestDeleteCalendarEventAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestDeleteCalendarJobAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestGetCalendarEventsAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestGetCalendarsAction;
@ -513,6 +516,7 @@ public class MachineLearning implements MachineLearningClientActionPlugin, Actio
new RestGetCalendarsAction(settings, restController),
new RestPutCalendarAction(settings, restController),
new RestDeleteCalendarAction(settings, restController),
new RestDeleteCalendarEventAction(settings, restController),
new RestDeleteCalendarJobAction(settings, restController),
new RestPutCalendarJobAction(settings, restController),
new RestGetCalendarEventsAction(settings, restController),
@ -566,6 +570,7 @@ public class MachineLearning implements MachineLearningClientActionPlugin, Actio
new ActionHandler<>(GetCalendarsAction.INSTANCE, TransportGetCalendarsAction.class),
new ActionHandler<>(PutCalendarAction.INSTANCE, TransportPutCalendarAction.class),
new ActionHandler<>(DeleteCalendarAction.INSTANCE, TransportDeleteCalendarAction.class),
new ActionHandler<>(DeleteCalendarEventAction.INSTANCE, TransportDeleteCalendarEventAction.class),
new ActionHandler<>(UpdateCalendarJobAction.INSTANCE, TransportUpdateCalendarJobAction.class),
new ActionHandler<>(GetCalendarEventsAction.INSTANCE, TransportGetCalendarEventsAction.class),
new ActionHandler<>(PostCalendarEventsAction.INSTANCE, TransportPostCalendarEventsAction.class)

View File

@ -7,18 +7,21 @@ package org.elasticsearch.xpack.ml.action;
import org.elasticsearch.ResourceNotFoundException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.bulk.BulkAction;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.ml.MlMetaIndex;
@ -47,27 +50,41 @@ public class TransportDeleteCalendarAction extends HandledTransportAction<Delete
final String calendarId = request.getCalendarId();
DeleteRequest deleteRequest = new DeleteRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, Calendar.documentId(calendarId));
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
bulkRequestBuilder.add(deleteRequest);
bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
executeAsyncWithOrigin(client, ML_ORIGIN, BulkAction.INSTANCE, bulkRequestBuilder.request(),
new ActionListener<BulkResponse>() {
GetRequest getRequest = new GetRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, Calendar.documentId(calendarId));
executeAsyncWithOrigin(client, ML_ORIGIN, GetAction.INSTANCE, getRequest, new ActionListener<GetResponse>() {
@Override
public void onResponse(BulkResponse bulkResponse) {
if (bulkResponse.getItems()[0].status() == RestStatus.NOT_FOUND) {
listener.onFailure(new ResourceNotFoundException("Could not delete calendar with ID [" + calendarId
public void onResponse(GetResponse getResponse) {
if (getResponse.isExists() == false) {
listener.onFailure(new ResourceNotFoundException("Could not delete calendar [" + calendarId
+ "] because it does not exist"));
} else {
listener.onResponse(new DeleteCalendarAction.Response(true));
return;
}
// Delete calendar and events
DeleteByQueryRequest dbqRequest = buildDeleteByQuery(calendarId);
executeAsyncWithOrigin(client, ML_ORIGIN, DeleteByQueryAction.INSTANCE, dbqRequest, ActionListener.wrap(
response -> listener.onResponse(new DeleteCalendarAction.Response(true)),
listener::onFailure));
}
@Override
public void onFailure(Exception e) {
listener.onFailure(ExceptionsHelper.serverError("Could not delete calendar with ID [" + calendarId + "]", e));
listener.onFailure(ExceptionsHelper.serverError("Could not delete calendar [" + calendarId + "]", e));
}
});
}
);
}
private DeleteByQueryRequest buildDeleteByQuery(String calendarId) {
SearchRequest searchRequest = new SearchRequest(MlMetaIndex.INDEX_NAME);
// The DBQ request constructor wipes the search request source
// so it has to be set after
DeleteByQueryRequest request = new DeleteByQueryRequest(searchRequest);
request.setSlices(5);
request.setRefresh(true);
QueryBuilder query = QueryBuilders.termsQuery(Calendar.ID.getPreferredName(), calendarId);
searchRequest.source(new SearchSourceBuilder().query(query));
return request;
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ml.action;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.ResourceNotFoundException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.ml.MlMetaIndex;
import org.elasticsearch.xpack.ml.calendars.Calendar;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
import java.util.Map;
import static org.elasticsearch.xpack.ClientHelper.ML_ORIGIN;
import static org.elasticsearch.xpack.ClientHelper.executeAsyncWithOrigin;
public class TransportDeleteCalendarEventAction extends HandledTransportAction<DeleteCalendarEventAction.Request,
DeleteCalendarEventAction.Response> {
private final Client client;
@Inject
public TransportDeleteCalendarEventAction(Settings settings, ThreadPool threadPool,
TransportService transportService, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver,
Client client) {
super(settings, DeleteCalendarEventAction.NAME, threadPool, transportService, actionFilters,
indexNameExpressionResolver, DeleteCalendarEventAction.Request::new);
this.client = client;
}
@Override
protected void doExecute(DeleteCalendarEventAction.Request request, ActionListener<DeleteCalendarEventAction.Response> listener) {
final String eventId = request.getEventId();
GetRequest getRequest = new GetRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, eventId);
executeAsyncWithOrigin(client, ML_ORIGIN, GetAction.INSTANCE, getRequest, new ActionListener<GetResponse>() {
@Override
public void onResponse(GetResponse getResponse) {
if (getResponse.isExists() == false) {
listener.onFailure(new ResourceNotFoundException("Missing event [" + eventId + "]"));
return;
}
Map<String, Object> source = getResponse.getSourceAsMap();
String calendarId = (String) source.get(Calendar.ID.getPreferredName());
if (calendarId == null) {
listener.onFailure(new ElasticsearchStatusException("Event [" + eventId + "] does not have a valid "
+ Calendar.ID.getPreferredName(), RestStatus.BAD_REQUEST));
return;
}
if (calendarId.equals(request.getCalendarId()) == false) {
listener.onFailure(new ElasticsearchStatusException(
"Event [" + eventId + "] has " + Calendar.ID.getPreferredName() +
" [" + calendarId + "] which does not match the request " + Calendar.ID.getPreferredName() +
" [" + request.getCalendarId() + "]", RestStatus.BAD_REQUEST));
return;
}
deleteEvent(eventId, listener);
}
@Override
public void onFailure(Exception e) {
listener.onFailure(e);
}
});
}
private void deleteEvent(String eventId, ActionListener<DeleteCalendarEventAction.Response> listener) {
DeleteRequest deleteRequest = new DeleteRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, eventId);
deleteRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
executeAsyncWithOrigin(client, ML_ORIGIN, DeleteAction.INSTANCE, deleteRequest,
new ActionListener<DeleteResponse>() {
@Override
public void onResponse(DeleteResponse response) {
if (response.status() == RestStatus.NOT_FOUND) {
listener.onFailure(new ResourceNotFoundException("Could not delete event [" + eventId
+ "] because it does not exist"));
} else {
listener.onResponse(new DeleteCalendarEventAction.Response(true));
}
}
@Override
public void onFailure(Exception e) {
listener.onFailure(ExceptionsHelper.serverError("Could not delete event [" + eventId + "]", e));
}
});
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ml.rest.calendar;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.AcknowledgedRestListener;
import org.elasticsearch.xpack.ml.MachineLearning;
import org.elasticsearch.xpack.ml.action.DeleteCalendarEventAction;
import org.elasticsearch.xpack.ml.calendars.Calendar;
import org.elasticsearch.xpack.ml.calendars.ScheduledEvent;
import java.io.IOException;
public class RestDeleteCalendarEventAction extends BaseRestHandler {
public RestDeleteCalendarEventAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(RestRequest.Method.DELETE,
MachineLearning.BASE_PATH + "calendars/{" + Calendar.ID.getPreferredName() + "}/events/{" +
ScheduledEvent.EVENT_ID.getPreferredName() + "}", this);
}
@Override
public String getName() {
return "xpack_ml_delete_calendar_event_action";
}
@Override
protected BaseRestHandler.RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
String eventId = restRequest.param(ScheduledEvent.EVENT_ID.getPreferredName());
String calendarId = restRequest.param(Calendar.ID.getPreferredName());
DeleteCalendarEventAction.Request request = new DeleteCalendarEventAction.Request(calendarId, eventId);
return channel -> client.execute(DeleteCalendarEventAction.INSTANCE, request, new AcknowledgedRestListener<>(channel));
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ml.action;
import org.elasticsearch.test.AbstractStreamableTestCase;
import org.elasticsearch.xpack.ml.action.DeleteCalendarEventAction.Request;
public class DeleteCalendarEventActionRequestTests extends AbstractStreamableTestCase<DeleteCalendarEventAction.Request> {
@Override
protected Request createTestInstance() {
return new Request(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
}
@Override
protected Request createBlankInstance() {
return new Request();
}
}

View File

@ -32,7 +32,7 @@ public class ScheduledEventTests extends AbstractSerializingTestCase<ScheduledEv
public static ScheduledEvent createScheduledEvent(String calendarId) {
ZonedDateTime start = ZonedDateTime.ofInstant(Instant.ofEpochMilli(new DateTime(randomDateTimeZone()).getMillis()), ZoneOffset.UTC);
return new ScheduledEvent(randomAlphaOfLength(10), start, start.plusSeconds(randomIntBetween(1, 10000)),
calendarId);
calendarId, null);
}
@Override

View File

@ -0,0 +1,22 @@
{
"xpack.ml.delete_calendar_event": {
"methods": [ "DELETE" ],
"url": {
"path": "/_xpack/ml/calendars/{calendar_id}/events/{event_id}",
"paths": [ "/_xpack/ml/calendars/{calendar_id}/events/{event_id}" ],
"parts": {
"calendar_id": {
"type" : "string",
"required" : true,
"description" : "The ID of the calendar to modify"
},
"event_id": {
"type": "string",
"required": true,
"description": "The ID of the event to remove from the calendar"
}
}
},
"body": null
}
}

View File

@ -241,7 +241,7 @@
job_id: "missing job"
---
"Test calendar events":
"Test calendar get events":
- do:
xpack.ml.put_calendar:
@ -264,6 +264,8 @@
- match: { events.1.description: "event 2" }
- match: { events.2.description: "event 3" }
- match: { events.3.description: "event 4" }
- is_true: events.0.event_id
- set: { events.0.event_id: event_1_id }
- do:
xpack.ml.get_calendar_events:
@ -299,6 +301,98 @@
- length: { events: 1 }
- match: { events.0.description: "event 2" }
- do:
xpack.ml.put_calendar:
calendar_id: "events-2"
- do:
xpack.ml.post_calendar_events:
calendar_id: "events-2"
body: >
{ "description": "event 21", "start_time": "2017-12-02T00:00:00Z", "end_time": "2017-12-02T05:00:00Z"}
{ "description": "event 22", "start_time": "2017-12-25T00:00:00Z", "end_time": "2017-12-26T00:00:00Z"}
- do:
catch: bad_request
xpack.ml.post_calendar_events:
calendar_id: "events-2"
body: >
{ "description": "event 21", "start_time": "2017-12-02T00:00:00Z", "end_time": "2017-12-03T00:00:00Z", "calendar_id": "events"}
# Event is not in calendar events-2
- do:
catch: bad_request
xpack.ml.delete_calendar_event:
calendar_id: "events-2"
event_id: $event_1_id
- do:
xpack.ml.delete_calendar_event:
calendar_id: "events"
event_id: $event_1_id
- do:
catch: missing
xpack.ml.delete_calendar_event:
calendar_id: "events"
event_id: "missing event"
---
"Test delete calendar deletes events":
- do:
xpack.ml.put_calendar:
calendar_id: "cal-foo"
- do:
xpack.ml.post_calendar_events:
calendar_id: "cal-foo"
body: >
{ "description": "event 1", "start_time": "2017-12-01T00:00:00Z", "end_time": "2017-12-02T00:00:00Z" }
{ "description": "event 2", "start_time": "2017-12-05T00:00:00Z", "end_time": "2017-12-06T00:00:00Z" }
{ "description": "event 2", "start_time": "2017-12-05T00:00:00Z", "end_time": "2017-12-06T00:00:00Z" }
- do:
xpack.ml.put_calendar:
calendar_id: "cal-bar"
- do:
xpack.ml.post_calendar_events:
calendar_id: "cal-bar"
body: >
{ "description": "event 21", "start_time": "2017-12-02T00:00:00Z", "end_time": "2017-12-02T05:00:00Z"}
{ "description": "event 22", "start_time": "2017-12-25T00:00:00Z", "end_time": "2017-12-26T00:00:00Z"}
- do:
xpack.ml.delete_calendar:
calendar_id: "cal-foo"
# Check the event from calendar 1 is deleted
- do:
count:
index: .ml-meta
body:
query:
constant_score:
filter:
term:
type: scheduled_event
- match: { count: 2 }
- do:
count:
index: .ml-meta
body:
query:
bool:
must:
- term:
type: scheduled_event
- term:
calendar_id: cal-foo
- match: { count: 0 }
---
"Test get all calendar events":
@ -386,6 +480,7 @@
{ "description": "random2", "start_time": "2018-02-20T00:00:00Z", "end_time": "2018-02-26T00:00:00Z" }
# Calendar Id must be _all if a job id is used
- do:
catch: /action_request_validation_exception/
xpack.ml.get_calendar_events: