[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; private String calendarId;
Request() { Request() {
} }
public Request(String calendarId) { public Request(String calendarId) {
@ -117,5 +116,4 @@ public class DeleteCalendarAction extends Action<DeleteCalendarAction.Request, D
writeAcknowledged(out); 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; package org.elasticsearch.xpack.ml.calendars;
import org.elasticsearch.common.ParseField; 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.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable; 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 START_TIME = new ParseField("start_time");
public static final ParseField END_TIME = new ParseField("end_time"); public static final ParseField END_TIME = new ParseField("end_time");
public static final ParseField TYPE = new ParseField("type"); 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"); 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 startTime;
private final ZonedDateTime endTime; private final ZonedDateTime endTime;
private final String calendarId; 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.description = Objects.requireNonNull(description);
this.startTime = Objects.requireNonNull(startTime); this.startTime = Objects.requireNonNull(startTime);
this.endTime = Objects.requireNonNull(endTime); this.endTime = Objects.requireNonNull(endTime);
this.calendarId = Objects.requireNonNull(calendarId); this.calendarId = Objects.requireNonNull(calendarId);
this.eventId = eventId;
} }
public ScheduledEvent(StreamInput in) throws IOException { 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); startTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.readVLong()), ZoneOffset.UTC);
endTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.readVLong()), ZoneOffset.UTC); endTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.readVLong()), ZoneOffset.UTC);
calendarId = in.readString(); calendarId = in.readString();
eventId = in.readOptionalString();
} }
public String getDescription() { public String getDescription() {
@ -112,6 +117,10 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
return calendarId; return calendarId;
} }
public String getEventId() {
return eventId;
}
/** /**
* Convert the scheduled event to a detection rule. * Convert the scheduled event to a detection rule.
* The rule will have 2 time based conditions for the start and * 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(startTime.toInstant().toEpochMilli());
out.writeVLong(endTime.toInstant().toEpochMilli()); out.writeVLong(endTime.toInstant().toEpochMilli());
out.writeString(calendarId); out.writeString(calendarId);
out.writeOptionalString(eventId);
} }
@Override @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(START_TIME.getPreferredName(), START_TIME.getPreferredName() + "_string", startTime.toInstant().toEpochMilli());
builder.dateField(END_TIME.getPreferredName(), END_TIME.getPreferredName() + "_string", endTime.toInstant().toEpochMilli()); builder.dateField(END_TIME.getPreferredName(), END_TIME.getPreferredName() + "_string", endTime.toInstant().toEpochMilli());
builder.field(Calendar.ID.getPreferredName(), calendarId); builder.field(Calendar.ID.getPreferredName(), calendarId);
if (eventId != null) {
builder.field(EVENT_ID.getPreferredName(), eventId);
}
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) { if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
builder.field(TYPE.getPreferredName(), SCHEDULED_EVENT_TYPE); builder.field(TYPE.getPreferredName(), SCHEDULED_EVENT_TYPE);
} }
@ -197,7 +210,7 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
private ZonedDateTime startTime; private ZonedDateTime startTime;
private ZonedDateTime endTime; private ZonedDateTime endTime;
private String calendarId; private String calendarId;
private String eventId;
public Builder description(String description) { public Builder description(String description) {
this.description = description; this.description = description;
@ -223,6 +236,11 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
return calendarId; return calendarId;
} }
public Builder eventId(String eventId) {
this.eventId = eventId;
return this;
}
public ScheduledEvent build() { public ScheduledEvent build() {
if (description == null) { if (description == null) {
throw ExceptionsHelper.badRequestException( throw ExceptionsHelper.badRequestException(
@ -249,7 +267,9 @@ public class ScheduledEvent implements ToXContentObject, Writeable {
"] must come before end time [" + endTime + "]"); "] 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<>(); List<ScheduledEvent> events = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits(); SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) { 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(), handler.onResponse(new QueryPage<>(events, response.getHits().getTotalHits(),
ScheduledEvent.RESULTS_FIELD)); ScheduledEvent.RESULTS_FIELD));
}, },
handler::onFailure) handler::onFailure),
, client::search); client::search);
} }
public void getForecastRequestStats(String jobId, String forecastId, Consumer<ForecastRequestStats> handler, public void getForecastRequestStats(String jobId, String forecastId, Consumer<ForecastRequestStats> handler,

View File

@ -24,8 +24,8 @@ import java.util.List;
public class ScheduledEventsQueryBuilder { public class ScheduledEventsQueryBuilder {
public static final int DEFAULT_SIZE = 1000; public static final int DEFAULT_SIZE = 1000;
private int from = 0; private Integer from = 0;
private int size = DEFAULT_SIZE; private Integer size = DEFAULT_SIZE;
private List<String> calendarIds; private List<String> calendarIds;
private String after; private String after;
@ -46,12 +46,22 @@ public class ScheduledEventsQueryBuilder {
return this; 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; this.from = from;
return this; 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; this.size = size;
return this; return this;
} }
@ -78,8 +88,12 @@ public class ScheduledEventsQueryBuilder {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.sort(ScheduledEvent.START_TIME.getPreferredName()); searchSourceBuilder.sort(ScheduledEvent.START_TIME.getPreferredName());
if (from != null) {
searchSourceBuilder.from(from); searchSourceBuilder.from(from);
}
if (size != null) {
searchSourceBuilder.size(size); searchSourceBuilder.size(size);
}
if (queries.isEmpty()) { if (queries.isEmpty()) {
searchSourceBuilder.query(typeQuery); searchSourceBuilder.query(typeQuery);

View File

@ -52,6 +52,11 @@ import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings; import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.ml.action.CloseJobAction; import org.elasticsearch.xpack.ml.action.CloseJobAction;
import org.elasticsearch.xpack.ml.action.DeleteCalendarAction; 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.DeleteDatafeedAction;
import org.elasticsearch.xpack.ml.action.DeleteExpiredDataAction; import org.elasticsearch.xpack.ml.action.DeleteExpiredDataAction;
import org.elasticsearch.xpack.ml.action.DeleteFilterAction; 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.FlushJobAction;
import org.elasticsearch.xpack.ml.action.ForecastJobAction; import org.elasticsearch.xpack.ml.action.ForecastJobAction;
import org.elasticsearch.xpack.ml.action.GetBucketsAction; 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.GetCalendarsAction;
import org.elasticsearch.xpack.ml.action.GetCategoriesAction; import org.elasticsearch.xpack.ml.action.GetCategoriesAction;
import org.elasticsearch.xpack.ml.action.GetDatafeedsAction; 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.IsolateDatafeedAction;
import org.elasticsearch.xpack.ml.action.KillProcessAction; import org.elasticsearch.xpack.ml.action.KillProcessAction;
import org.elasticsearch.xpack.ml.action.OpenJobAction; 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.PostDataAction;
import org.elasticsearch.xpack.ml.action.PreviewDatafeedAction; import org.elasticsearch.xpack.ml.action.PreviewDatafeedAction;
import org.elasticsearch.xpack.ml.action.PutCalendarAction; 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.TransportUpdateProcessAction;
import org.elasticsearch.xpack.ml.action.TransportValidateDetectorAction; import org.elasticsearch.xpack.ml.action.TransportValidateDetectorAction;
import org.elasticsearch.xpack.ml.action.TransportValidateJobConfigAction; 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.UpdateDatafeedAction;
import org.elasticsearch.xpack.ml.action.UpdateJobAction; import org.elasticsearch.xpack.ml.action.UpdateJobAction;
import org.elasticsearch.xpack.ml.action.UpdateModelSnapshotAction; 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.notifications.Auditor;
import org.elasticsearch.xpack.ml.rest.RestDeleteExpiredDataAction; import org.elasticsearch.xpack.ml.rest.RestDeleteExpiredDataAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestDeleteCalendarAction; 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.RestDeleteCalendarJobAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestGetCalendarEventsAction; import org.elasticsearch.xpack.ml.rest.calendar.RestGetCalendarEventsAction;
import org.elasticsearch.xpack.ml.rest.calendar.RestGetCalendarsAction; import org.elasticsearch.xpack.ml.rest.calendar.RestGetCalendarsAction;
@ -513,6 +516,7 @@ public class MachineLearning implements MachineLearningClientActionPlugin, Actio
new RestGetCalendarsAction(settings, restController), new RestGetCalendarsAction(settings, restController),
new RestPutCalendarAction(settings, restController), new RestPutCalendarAction(settings, restController),
new RestDeleteCalendarAction(settings, restController), new RestDeleteCalendarAction(settings, restController),
new RestDeleteCalendarEventAction(settings, restController),
new RestDeleteCalendarJobAction(settings, restController), new RestDeleteCalendarJobAction(settings, restController),
new RestPutCalendarJobAction(settings, restController), new RestPutCalendarJobAction(settings, restController),
new RestGetCalendarEventsAction(settings, restController), new RestGetCalendarEventsAction(settings, restController),
@ -566,6 +570,7 @@ public class MachineLearning implements MachineLearningClientActionPlugin, Actio
new ActionHandler<>(GetCalendarsAction.INSTANCE, TransportGetCalendarsAction.class), new ActionHandler<>(GetCalendarsAction.INSTANCE, TransportGetCalendarsAction.class),
new ActionHandler<>(PutCalendarAction.INSTANCE, TransportPutCalendarAction.class), new ActionHandler<>(PutCalendarAction.INSTANCE, TransportPutCalendarAction.class),
new ActionHandler<>(DeleteCalendarAction.INSTANCE, TransportDeleteCalendarAction.class), new ActionHandler<>(DeleteCalendarAction.INSTANCE, TransportDeleteCalendarAction.class),
new ActionHandler<>(DeleteCalendarEventAction.INSTANCE, TransportDeleteCalendarEventAction.class),
new ActionHandler<>(UpdateCalendarJobAction.INSTANCE, TransportUpdateCalendarJobAction.class), new ActionHandler<>(UpdateCalendarJobAction.INSTANCE, TransportUpdateCalendarJobAction.class),
new ActionHandler<>(GetCalendarEventsAction.INSTANCE, TransportGetCalendarEventsAction.class), new ActionHandler<>(GetCalendarEventsAction.INSTANCE, TransportGetCalendarEventsAction.class),
new ActionHandler<>(PostCalendarEventsAction.INSTANCE, TransportPostCalendarEventsAction.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.ResourceNotFoundException;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.bulk.BulkAction; import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; 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.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.ml.MlMetaIndex; import org.elasticsearch.xpack.ml.MlMetaIndex;
@ -47,27 +50,41 @@ public class TransportDeleteCalendarAction extends HandledTransportAction<Delete
final String calendarId = request.getCalendarId(); final String calendarId = request.getCalendarId();
DeleteRequest deleteRequest = new DeleteRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, Calendar.documentId(calendarId)); GetRequest getRequest = new GetRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, Calendar.documentId(calendarId));
executeAsyncWithOrigin(client, ML_ORIGIN, GetAction.INSTANCE, getRequest, new ActionListener<GetResponse>() {
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
bulkRequestBuilder.add(deleteRequest);
bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
executeAsyncWithOrigin(client, ML_ORIGIN, BulkAction.INSTANCE, bulkRequestBuilder.request(),
new ActionListener<BulkResponse>() {
@Override @Override
public void onResponse(BulkResponse bulkResponse) { public void onResponse(GetResponse getResponse) {
if (bulkResponse.getItems()[0].status() == RestStatus.NOT_FOUND) { if (getResponse.isExists() == false) {
listener.onFailure(new ResourceNotFoundException("Could not delete calendar with ID [" + calendarId listener.onFailure(new ResourceNotFoundException("Could not delete calendar [" + calendarId
+ "] because it does not exist")); + "] because it does not exist"));
} else { return;
listener.onResponse(new DeleteCalendarAction.Response(true));
} }
// 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 @Override
public void onFailure(Exception e) { 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) { public static ScheduledEvent createScheduledEvent(String calendarId) {
ZonedDateTime start = ZonedDateTime.ofInstant(Instant.ofEpochMilli(new DateTime(randomDateTimeZone()).getMillis()), ZoneOffset.UTC); ZonedDateTime start = ZonedDateTime.ofInstant(Instant.ofEpochMilli(new DateTime(randomDateTimeZone()).getMillis()), ZoneOffset.UTC);
return new ScheduledEvent(randomAlphaOfLength(10), start, start.plusSeconds(randomIntBetween(1, 10000)), return new ScheduledEvent(randomAlphaOfLength(10), start, start.plusSeconds(randomIntBetween(1, 10000)),
calendarId); calendarId, null);
} }
@Override @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" job_id: "missing job"
--- ---
"Test calendar events": "Test calendar get events":
- do: - do:
xpack.ml.put_calendar: xpack.ml.put_calendar:
@ -264,6 +264,8 @@
- match: { events.1.description: "event 2" } - match: { events.1.description: "event 2" }
- match: { events.2.description: "event 3" } - match: { events.2.description: "event 3" }
- match: { events.3.description: "event 4" } - match: { events.3.description: "event 4" }
- is_true: events.0.event_id
- set: { events.0.event_id: event_1_id }
- do: - do:
xpack.ml.get_calendar_events: xpack.ml.get_calendar_events:
@ -299,6 +301,98 @@
- length: { events: 1 } - length: { events: 1 }
- match: { events.0.description: "event 2" } - 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": "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" } { "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: - do:
catch: /action_request_validation_exception/ catch: /action_request_validation_exception/
xpack.ml.get_calendar_events: xpack.ml.get_calendar_events: