[ML] Special events calendar CRUD endpoints (elastic/x-pack-elasticsearch#3267)
* Calendar CRUD endpoints * Get calendars requires monitor permission * Address review comments * Add page params to get calendars Original commit: elastic/x-pack-elasticsearch@badd1e6add
This commit is contained in:
parent
249d06b256
commit
6113b86bdb
|
@ -52,6 +52,7 @@ import org.elasticsearch.xpack.XPackFeatureSet;
|
|||
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.DeleteDatafeedAction;
|
||||
import org.elasticsearch.xpack.ml.action.DeleteExpiredDataAction;
|
||||
import org.elasticsearch.xpack.ml.action.DeleteFilterAction;
|
||||
|
@ -61,6 +62,7 @@ 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.GetCalendarsAction;
|
||||
import org.elasticsearch.xpack.ml.action.GetCategoriesAction;
|
||||
import org.elasticsearch.xpack.ml.action.GetDatafeedsAction;
|
||||
import org.elasticsearch.xpack.ml.action.GetDatafeedsStatsAction;
|
||||
|
@ -76,6 +78,7 @@ import org.elasticsearch.xpack.ml.action.KillProcessAction;
|
|||
import org.elasticsearch.xpack.ml.action.OpenJobAction;
|
||||
import org.elasticsearch.xpack.ml.action.PostDataAction;
|
||||
import org.elasticsearch.xpack.ml.action.PreviewDatafeedAction;
|
||||
import org.elasticsearch.xpack.ml.action.PutCalendarAction;
|
||||
import org.elasticsearch.xpack.ml.action.PutDatafeedAction;
|
||||
import org.elasticsearch.xpack.ml.action.PutFilterAction;
|
||||
import org.elasticsearch.xpack.ml.action.PutJobAction;
|
||||
|
@ -114,6 +117,9 @@ import org.elasticsearch.xpack.ml.job.process.normalizer.NormalizerProcessFactor
|
|||
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.RestGetCalendarsAction;
|
||||
import org.elasticsearch.xpack.ml.rest.calendar.RestPutCalendarAction;
|
||||
import org.elasticsearch.xpack.ml.rest.datafeeds.RestDeleteDatafeedAction;
|
||||
import org.elasticsearch.xpack.ml.rest.datafeeds.RestGetDatafeedStatsAction;
|
||||
import org.elasticsearch.xpack.ml.rest.datafeeds.RestGetDatafeedsAction;
|
||||
|
@ -458,7 +464,10 @@ public class MachineLearning implements ActionPlugin {
|
|||
new RestStopDatafeedAction(settings, restController),
|
||||
new RestDeleteModelSnapshotAction(settings, restController),
|
||||
new RestDeleteExpiredDataAction(settings, restController),
|
||||
new RestForecastJobAction(settings, restController)
|
||||
new RestForecastJobAction(settings, restController),
|
||||
new RestGetCalendarsAction(settings, restController),
|
||||
new RestPutCalendarAction(settings, restController),
|
||||
new RestDeleteCalendarAction(settings, restController)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -504,7 +513,10 @@ public class MachineLearning implements ActionPlugin {
|
|||
new ActionHandler<>(DeleteModelSnapshotAction.INSTANCE, DeleteModelSnapshotAction.TransportAction.class),
|
||||
new ActionHandler<>(UpdateProcessAction.INSTANCE, UpdateProcessAction.TransportAction.class),
|
||||
new ActionHandler<>(DeleteExpiredDataAction.INSTANCE, DeleteExpiredDataAction.TransportAction.class),
|
||||
new ActionHandler<>(ForecastJobAction.INSTANCE, ForecastJobAction.TransportAction.class)
|
||||
new ActionHandler<>(ForecastJobAction.INSTANCE, ForecastJobAction.TransportAction.class),
|
||||
new ActionHandler<>(GetCalendarsAction.INSTANCE, GetCalendarsAction.TransportAction.class),
|
||||
new ActionHandler<>(PutCalendarAction.INSTANCE, PutCalendarAction.TransportAction.class),
|
||||
new ActionHandler<>(DeleteCalendarAction.INSTANCE, DeleteCalendarAction.TransportAction.class)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ public final class MlMetaIndex {
|
|||
*/
|
||||
public static final String INDEX_NAME = ".ml-meta";
|
||||
|
||||
public static final String INCLUDE_TYPE_KEY = "include_type";
|
||||
|
||||
public static final String TYPE = "doc";
|
||||
|
||||
private MlMetaIndex() {}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* 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.ResourceNotFoundException;
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
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.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
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.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.xpack.ClientHelper.ML_ORIGIN;
|
||||
import static org.elasticsearch.xpack.ClientHelper.executeAsyncWithOrigin;
|
||||
|
||||
public class DeleteCalendarAction extends Action<DeleteCalendarAction.Request, DeleteCalendarAction.Response,
|
||||
DeleteCalendarAction.RequestBuilder> {
|
||||
|
||||
public static final DeleteCalendarAction INSTANCE = new DeleteCalendarAction();
|
||||
public static final String NAME = "cluster:admin/xpack/ml/calendars/delete";
|
||||
|
||||
private DeleteCalendarAction() {
|
||||
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;
|
||||
|
||||
Request() {
|
||||
|
||||
}
|
||||
|
||||
public Request(String calendarId) {
|
||||
this.calendarId = ExceptionsHelper.requireNonNull(calendarId, Calendar.ID.getPreferredName());
|
||||
}
|
||||
|
||||
public String getCalendarId() {
|
||||
return calendarId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
calendarId = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeString(calendarId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(calendarId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Request other = (Request) obj;
|
||||
return Objects.equals(calendarId, other.calendarId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequestBuilder extends ActionRequestBuilder<Request, Response,
|
||||
RequestBuilder> {
|
||||
|
||||
public RequestBuilder(ElasticsearchClient client, DeleteCalendarAction 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);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransportAction extends HandledTransportAction<DeleteCalendarAction.Request, DeleteCalendarAction.Response> {
|
||||
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
public TransportAction(Settings settings, ThreadPool threadPool,
|
||||
TransportService transportService, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
Client client) {
|
||||
super(settings, NAME, threadPool, transportService, actionFilters,
|
||||
indexNameExpressionResolver, Request::new);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(DeleteCalendarAction.Request request, ActionListener<DeleteCalendarAction.Response> listener) {
|
||||
|
||||
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>() {
|
||||
@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
|
||||
+ "] because it does not exist"));
|
||||
} else {
|
||||
listener.onResponse(new Response(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(ExceptionsHelper.serverError("Could not delete calendar with ID [" + calendarId + "]", e));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* 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.ActionListener;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
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.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.StatusToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.ml.MlMetaIndex;
|
||||
import org.elasticsearch.xpack.ml.action.util.PageParams;
|
||||
import org.elasticsearch.xpack.ml.action.util.QueryPage;
|
||||
import org.elasticsearch.xpack.ml.calendars.Calendar;
|
||||
import org.elasticsearch.xpack.ml.job.persistence.JobProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.action.ValidateActions.addValidationError;
|
||||
import static org.elasticsearch.xpack.ClientHelper.ML_ORIGIN;
|
||||
import static org.elasticsearch.xpack.ClientHelper.executeAsyncWithOrigin;
|
||||
|
||||
public class GetCalendarsAction extends Action<GetCalendarsAction.Request, GetCalendarsAction.Response, GetCalendarsAction.RequestBuilder> {
|
||||
|
||||
public static final GetCalendarsAction INSTANCE = new GetCalendarsAction();
|
||||
public static final String NAME = "cluster:monitor/xpack/ml/calendars/get";
|
||||
|
||||
private GetCalendarsAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new RequestBuilder(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response newResponse() {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
public static class Request extends ActionRequest {
|
||||
|
||||
private String calendarId;
|
||||
private PageParams pageParams;
|
||||
|
||||
public Request() {
|
||||
}
|
||||
|
||||
public void setCalendarId(String calendarId) {
|
||||
this.calendarId = calendarId;
|
||||
}
|
||||
|
||||
public String getCalendarId() {
|
||||
return calendarId;
|
||||
}
|
||||
|
||||
public PageParams getPageParams() {
|
||||
return pageParams;
|
||||
}
|
||||
|
||||
public void setPageParams(PageParams pageParams) {
|
||||
this.pageParams = pageParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = null;
|
||||
|
||||
if (calendarId != null && pageParams != null) {
|
||||
validationException = addValidationError("Params [" + PageParams.FROM.getPreferredName()
|
||||
+ ", " + PageParams.SIZE.getPreferredName() + "] are incompatible with ["
|
||||
+ Calendar.ID.getPreferredName() + "].",
|
||||
validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
calendarId = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeString(calendarId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(calendarId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Request other = (Request) obj;
|
||||
return Objects.equals(calendarId, other.calendarId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder> {
|
||||
|
||||
public RequestBuilder(ElasticsearchClient client) {
|
||||
super(client, INSTANCE, new Request());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends ActionResponse implements StatusToXContentObject {
|
||||
|
||||
private QueryPage<Calendar> calendars;
|
||||
|
||||
public Response(QueryPage<Calendar> calendars) {
|
||||
this.calendars = calendars;
|
||||
}
|
||||
|
||||
Response() {
|
||||
}
|
||||
|
||||
public QueryPage<Calendar> getCalendars() {
|
||||
return calendars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
calendars = new QueryPage<>(in, Calendar::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
calendars.writeTo(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestStatus status() {
|
||||
return RestStatus.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
calendars.doXContentBody(builder, params);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(calendars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Response other = (Response) obj;
|
||||
return Objects.equals(calendars, other.calendars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return Strings.toString(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransportAction extends HandledTransportAction<Request, Response> {
|
||||
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
public TransportAction(Settings settings, ThreadPool threadPool,
|
||||
TransportService transportService, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
Client client) {
|
||||
super(settings, NAME, threadPool, transportService, actionFilters,
|
||||
indexNameExpressionResolver, Request::new);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(Request request, ActionListener<Response> listener) {
|
||||
final String calendarId = request.getCalendarId();
|
||||
if (request.getCalendarId() != null) {
|
||||
getCalendar(calendarId, listener);
|
||||
} else {
|
||||
PageParams pageParams = request.getPageParams();
|
||||
if (pageParams == null) {
|
||||
pageParams = PageParams.defaultParams();
|
||||
}
|
||||
getCalendars(pageParams, listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void getCalendar(String calendarId, ActionListener<Response> listener) {
|
||||
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(GetResponse getDocResponse) {
|
||||
|
||||
try {
|
||||
QueryPage<Calendar> calendars;
|
||||
if (getDocResponse.isExists()) {
|
||||
BytesReference docSource = getDocResponse.getSourceAsBytesRef();
|
||||
|
||||
try (XContentParser parser =
|
||||
XContentFactory.xContent(docSource).createParser(NamedXContentRegistry.EMPTY, docSource)) {
|
||||
Calendar calendar = Calendar.PARSER.apply(parser, null).build();
|
||||
calendars = new QueryPage<>(Collections.singletonList(calendar), 1, Calendar.RESULTS_FIELD);
|
||||
|
||||
Response response = new Response(calendars);
|
||||
listener.onResponse(response);
|
||||
}
|
||||
} else {
|
||||
this.onFailure(QueryPage.emptyQueryPage(Calendar.RESULTS_FIELD));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
this.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getCalendars(PageParams pageParams, ActionListener<Response> listener) {
|
||||
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
|
||||
.from(pageParams.getFrom())
|
||||
.size(pageParams.getSize())
|
||||
.sort(Calendar.ID.getPreferredName())
|
||||
.query(QueryBuilders.termQuery(Calendar.TYPE.getPreferredName(), Calendar.CALENDAR_TYPE));
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest(MlMetaIndex.INDEX_NAME)
|
||||
.indicesOptions(JobProvider.addIgnoreUnavailable(SearchRequest.DEFAULT_INDICES_OPTIONS))
|
||||
.source(sourceBuilder);
|
||||
|
||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), ML_ORIGIN, searchRequest, new ActionListener<SearchResponse>() {
|
||||
@Override
|
||||
public void onResponse(SearchResponse response) {
|
||||
List<Calendar> docs = new ArrayList<>();
|
||||
for (SearchHit hit : response.getHits().getHits()) {
|
||||
BytesReference docSource = hit.getSourceRef();
|
||||
try (XContentParser parser = XContentFactory.xContent(docSource).createParser(
|
||||
NamedXContentRegistry.EMPTY, docSource)) {
|
||||
docs.add(Calendar.PARSER.apply(parser, null).build());
|
||||
} catch (IOException e) {
|
||||
this.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
Response getResponse = new Response(
|
||||
new QueryPage<>(docs, docs.size(), Calendar.RESULTS_FIELD));
|
||||
listener.onResponse(getResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
},
|
||||
client::search);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* 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.ActionListener;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.DocWriteRequest;
|
||||
import org.elasticsearch.action.index.IndexAction;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
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.job.messages.Messages;
|
||||
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.action.ValidateActions.addValidationError;
|
||||
import static org.elasticsearch.xpack.ClientHelper.ML_ORIGIN;
|
||||
import static org.elasticsearch.xpack.ClientHelper.executeAsyncWithOrigin;
|
||||
|
||||
public class PutCalendarAction extends Action<PutCalendarAction.Request, PutCalendarAction.Response, PutCalendarAction.RequestBuilder> {
|
||||
public static final PutCalendarAction INSTANCE = new PutCalendarAction();
|
||||
public static final String NAME = "cluster:admin/xpack/ml/calendars/put";
|
||||
|
||||
private PutCalendarAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new RequestBuilder(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response newResponse() {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
public static class Request extends ActionRequest implements ToXContentObject {
|
||||
|
||||
public static Request parseRequest(String calendarId, XContentParser parser) {
|
||||
Calendar.Builder builder = Calendar.PARSER.apply(parser, null);
|
||||
if (builder.getId() == null) {
|
||||
builder.setId(calendarId);
|
||||
} else if (!Strings.isNullOrEmpty(calendarId) && !calendarId.equals(builder.getId())) {
|
||||
// If we have both URI and body filter ID, they must be identical
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.INCONSISTENT_ID, Calendar.ID.getPreferredName(),
|
||||
builder.getId(), calendarId));
|
||||
}
|
||||
return new Request(builder.build());
|
||||
}
|
||||
|
||||
private Calendar calendar;
|
||||
|
||||
Request() {
|
||||
|
||||
}
|
||||
|
||||
public Request(Calendar calendar) {
|
||||
this.calendar = ExceptionsHelper.requireNonNull(calendar, "calendar");
|
||||
}
|
||||
|
||||
public Calendar getCalendar() {
|
||||
return calendar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = null;
|
||||
if ("_all".equals(calendar.getId())) {
|
||||
validationException =
|
||||
addValidationError("Cannot create a Calendar with the reserved name [_all]",
|
||||
validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
calendar = new Calendar(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
calendar.writeTo(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
calendar.toXContent(builder, params);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(calendar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Request other = (Request) obj;
|
||||
return Objects.equals(calendar, other.calendar);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder> {
|
||||
|
||||
public RequestBuilder(ElasticsearchClient client) {
|
||||
super(client, INSTANCE, new Request());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends AcknowledgedResponse implements ToXContentObject {
|
||||
|
||||
private Calendar calendar;
|
||||
|
||||
Response() {
|
||||
}
|
||||
|
||||
public Response(Calendar calendar) {
|
||||
super(true);
|
||||
this.calendar = calendar;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return calendar.toXContent(builder, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransportAction extends HandledTransportAction<Request, Response> {
|
||||
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
public TransportAction(Settings settings, ThreadPool threadPool,
|
||||
TransportService transportService, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, Client client) {
|
||||
super(settings, NAME, threadPool, transportService, actionFilters,
|
||||
indexNameExpressionResolver, Request::new);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(Request request, ActionListener<Response> listener) {
|
||||
final Calendar calendar = request.getCalendar();
|
||||
IndexRequest indexRequest = new IndexRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, calendar.documentId());
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
indexRequest.source(calendar.toXContent(builder,
|
||||
new ToXContent.MapParams(Collections.singletonMap(MlMetaIndex.INCLUDE_TYPE_KEY, "true"))));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to serialise calendar with id [" + calendar.getId() + "]", e);
|
||||
}
|
||||
|
||||
// Make it an error to overwrite an existing calendar
|
||||
indexRequest.opType(DocWriteRequest.OpType.CREATE);
|
||||
indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
|
||||
|
||||
executeAsyncWithOrigin(client, ML_ORIGIN, IndexAction.INSTANCE, indexRequest,
|
||||
new ActionListener<IndexResponse>() {
|
||||
@Override
|
||||
public void onResponse(IndexResponse indexResponse) {
|
||||
listener.onResponse(new Response(calendar));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -181,7 +181,7 @@ public class PutFilterAction extends Action<PutFilterAction.Request, PutFilterAc
|
|||
MlFilter filter = request.getFilter();
|
||||
IndexRequest indexRequest = new IndexRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, filter.documentId());
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
ToXContent.MapParams params = new ToXContent.MapParams(Collections.singletonMap(MlFilter.INCLUDE_TYPE_KEY, "true"));
|
||||
ToXContent.MapParams params = new ToXContent.MapParams(Collections.singletonMap(MlMetaIndex.INCLUDE_TYPE_KEY, "true"));
|
||||
indexRequest.source(filter.toXContent(builder, params));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to serialise filter with id [" + filter.getId() + "]", e);
|
||||
|
|
|
@ -25,7 +25,6 @@ public class PageParams implements ToXContentObject, Writeable {
|
|||
public static final int DEFAULT_FROM = 0;
|
||||
public static final int DEFAULT_SIZE = 100;
|
||||
|
||||
|
||||
public static final ConstructingObjectParser<PageParams, Void> PARSER = new ConstructingObjectParser<>(PAGE.getPreferredName(),
|
||||
a -> new PageParams(a[0] == null ? DEFAULT_FROM : (int) a[0], a[1] == null ? DEFAULT_SIZE : (int) a[1]));
|
||||
|
||||
|
@ -39,6 +38,10 @@ public class PageParams implements ToXContentObject, Writeable {
|
|||
private final int from;
|
||||
private final int size;
|
||||
|
||||
public static PageParams defaultParams() {
|
||||
return new PageParams(DEFAULT_FROM, DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
public PageParams(StreamInput in) throws IOException {
|
||||
this(in.readVInt(), in.readVInt());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.calendars;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.ml.MlMetaIndex;
|
||||
import org.elasticsearch.xpack.ml.job.config.MlFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Calendar implements ToXContentObject, Writeable {
|
||||
|
||||
public static final String CALENDAR_TYPE = "calendar";
|
||||
|
||||
public static final ParseField TYPE = new ParseField("type");
|
||||
public static final ParseField ID = new ParseField("calendar_id");
|
||||
public static final ParseField JOB_IDS = new ParseField("job_ids");
|
||||
|
||||
private static final String DOCUMENT_ID_PREFIX = "calendar_";
|
||||
|
||||
// For QueryPage
|
||||
public static final ParseField RESULTS_FIELD = new ParseField("calendars");
|
||||
|
||||
public static final ObjectParser<Builder, Void> PARSER =
|
||||
new ObjectParser<>(ID.getPreferredName(), Calendar.Builder::new);
|
||||
|
||||
static {
|
||||
PARSER.declareString(Calendar.Builder::setId, ID);
|
||||
PARSER.declareStringArray(Calendar.Builder::setJobIds, JOB_IDS);
|
||||
PARSER.declareString((builder, s) -> {}, TYPE);
|
||||
}
|
||||
|
||||
public static String documentId(String calendarId) {
|
||||
return DOCUMENT_ID_PREFIX + calendarId;
|
||||
}
|
||||
|
||||
private final String id;
|
||||
private final List<String> jobIds;
|
||||
|
||||
public Calendar(String id, List<String> jobIds) {
|
||||
this.id = Objects.requireNonNull(id, ID.getPreferredName() + " must not be null");
|
||||
this.jobIds = Objects.requireNonNull(jobIds, JOB_IDS.getPreferredName() + " must not be null");
|
||||
}
|
||||
|
||||
public Calendar(StreamInput in) throws IOException {
|
||||
id = in.readString();
|
||||
jobIds = Arrays.asList(in.readStringArray());
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String documentId() {
|
||||
return documentId(id);
|
||||
}
|
||||
|
||||
public List<String> getJobIds() {
|
||||
return new ArrayList<>(jobIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeString(id);
|
||||
out.writeStringArray(jobIds.toArray(new String[jobIds.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(ID.getPreferredName(), id);
|
||||
builder.field(JOB_IDS.getPreferredName(), jobIds);
|
||||
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
|
||||
builder.field(TYPE.getPreferredName(), CALENDAR_TYPE);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Calendar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Calendar other = (Calendar) obj;
|
||||
return id.equals(other.id) && jobIds.equals(other.jobIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, jobIds);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String calendarId;
|
||||
private List<String> jobIds = Collections.emptyList();
|
||||
|
||||
public String getId() {
|
||||
return this.calendarId;
|
||||
}
|
||||
|
||||
public void setId(String calendarId) {
|
||||
this.calendarId = calendarId;
|
||||
}
|
||||
|
||||
public Builder setJobIds(List<String> jobIds) {
|
||||
this.jobIds = jobIds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Calendar build() {
|
||||
return new Calendar(calendarId, jobIds);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.common.xcontent.ObjectParser;
|
|||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.xpack.ml.MlMetaIndex;
|
||||
import org.elasticsearch.xpack.ml.job.config.Connective;
|
||||
import org.elasticsearch.xpack.ml.job.config.DetectionRule;
|
||||
import org.elasticsearch.xpack.ml.job.config.Operator;
|
||||
|
@ -171,7 +172,9 @@ public class SpecialEvent 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(JOB_IDS.getPreferredName(), jobIds);
|
||||
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
|
||||
builder.field(TYPE.getPreferredName(), SPECIAL_EVENT_TYPE);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.io.stream.Writeable;
|
|||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.ml.MlMetaIndex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -25,7 +26,6 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||
|
||||
public static final String DOCUMENT_ID_PREFIX = "filter_";
|
||||
|
||||
public static final String INCLUDE_TYPE_KEY = "include_type";
|
||||
public static final String FILTER_TYPE = "filter";
|
||||
|
||||
public static final ParseField TYPE = new ParseField("type");
|
||||
|
@ -67,7 +67,7 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||
builder.startObject();
|
||||
builder.field(ID.getPreferredName(), id);
|
||||
builder.field(ITEMS.getPreferredName(), items);
|
||||
if (params.paramAsBoolean(INCLUDE_TYPE_KEY, false)) {
|
||||
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
|
||||
builder.field(TYPE.getPreferredName(), FILTER_TYPE);
|
||||
}
|
||||
builder.endObject();
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.DeleteCalendarAction;
|
||||
import org.elasticsearch.xpack.ml.calendars.Calendar;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RestDeleteCalendarAction extends BaseRestHandler {
|
||||
|
||||
public RestDeleteCalendarAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(RestRequest.Method.DELETE,
|
||||
MachineLearning.BASE_PATH + "calendars/{" + Calendar.ID.getPreferredName() + "}", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "xpack_ml_delete_calendar_action";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
|
||||
DeleteCalendarAction.Request request = new DeleteCalendarAction.Request(restRequest.param(Calendar.ID.getPreferredName()));
|
||||
return channel -> client.execute(DeleteCalendarAction.INSTANCE, request, new AcknowledgedRestListener<>(channel));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.Strings;
|
||||
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.RestStatusToXContentListener;
|
||||
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||
import org.elasticsearch.xpack.ml.action.GetCalendarsAction;
|
||||
import org.elasticsearch.xpack.ml.action.util.PageParams;
|
||||
import org.elasticsearch.xpack.ml.calendars.Calendar;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RestGetCalendarsAction extends BaseRestHandler {
|
||||
|
||||
public RestGetCalendarsAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(RestRequest.Method.GET, MachineLearning.BASE_PATH + "calendars/{" + Calendar.ID.getPreferredName() + "}",
|
||||
this);
|
||||
controller.registerHandler(RestRequest.Method.GET, MachineLearning.BASE_PATH + "calendars/", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "xpack_ml_get_calendars_action";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
|
||||
GetCalendarsAction.Request getRequest = new GetCalendarsAction.Request();
|
||||
String calendarId = restRequest.param(Calendar.ID.getPreferredName());
|
||||
if (!Strings.isNullOrEmpty(calendarId)) {
|
||||
getRequest.setCalendarId(calendarId);
|
||||
}
|
||||
|
||||
if (restRequest.hasParam(PageParams.FROM.getPreferredName()) || restRequest.hasParam(PageParams.SIZE.getPreferredName())) {
|
||||
getRequest.setPageParams(new PageParams(restRequest.paramAsInt(PageParams.FROM.getPreferredName(), PageParams.DEFAULT_FROM),
|
||||
restRequest.paramAsInt(PageParams.SIZE.getPreferredName(), PageParams.DEFAULT_SIZE)));
|
||||
}
|
||||
|
||||
return channel -> client.execute(GetCalendarsAction.INSTANCE, getRequest, new RestStatusToXContentListener<>(channel));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.RestToXContentListener;
|
||||
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||
import org.elasticsearch.xpack.ml.action.PutCalendarAction;
|
||||
import org.elasticsearch.xpack.ml.calendars.Calendar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
public class RestPutCalendarAction extends BaseRestHandler {
|
||||
|
||||
public RestPutCalendarAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(RestRequest.Method.PUT,
|
||||
MachineLearning.BASE_PATH + "calendars/{" + Calendar.ID.getPreferredName() + "}", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "xpack_ml_put_calendar_action";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
|
||||
String calendarId = restRequest.param(Calendar.ID.getPreferredName());
|
||||
|
||||
PutCalendarAction.Request putCalendarRequest;
|
||||
// A calendar can be created with just a name or with an optional body
|
||||
if (restRequest.hasContentOrSourceParam()) {
|
||||
XContentParser parser = restRequest.contentOrSourceParamParser();
|
||||
putCalendarRequest = PutCalendarAction.Request.parseRequest(calendarId, parser);
|
||||
} else {
|
||||
putCalendarRequest = new PutCalendarAction.Request(new Calendar(calendarId, Collections.emptyList()));
|
||||
}
|
||||
|
||||
return channel -> client.execute(PutCalendarAction.INSTANCE, putCalendarRequest, new RestToXContentListener<>(channel));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class GetCalendarsActionRequestTests extends AbstractStreamableTestCase<GetCalendarsAction.Request> {
|
||||
|
||||
|
||||
@Override
|
||||
protected GetCalendarsAction.Request createTestInstance() {
|
||||
GetCalendarsAction.Request request = new GetCalendarsAction.Request();
|
||||
request.setCalendarId(randomAlphaOfLengthBetween(1, 20));
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetCalendarsAction.Request createBlankInstance() {
|
||||
return new GetCalendarsAction.Request();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
import org.elasticsearch.xpack.ml.calendars.Calendar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PutCalendarActionRequestTests extends AbstractStreamableXContentTestCase<PutCalendarAction.Request> {
|
||||
|
||||
private final String calendarId = randomAlphaOfLengthBetween(1, 20);
|
||||
|
||||
@Override
|
||||
protected PutCalendarAction.Request createTestInstance() {
|
||||
int size = randomInt(10);
|
||||
List<String> jobIds = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
jobIds.add(randomAlphaOfLengthBetween(1, 20));
|
||||
}
|
||||
Calendar calendar = new Calendar(calendarId, jobIds);
|
||||
return new PutCalendarAction.Request(calendar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutCalendarAction.Request createBlankInstance() {
|
||||
return new PutCalendarAction.Request();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutCalendarAction.Request doParseInstance(XContentParser parser) {
|
||||
return PutCalendarAction.Request.parseRequest(calendarId, parser);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.calendars;
|
||||
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractSerializingTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class CalendarTests extends AbstractSerializingTestCase<Calendar> {
|
||||
|
||||
@Override
|
||||
protected Calendar createTestInstance() {
|
||||
int size = randomInt(10);
|
||||
List<String> items = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
items.add(randomAlphaOfLengthBetween(1, 20));
|
||||
}
|
||||
return new Calendar(randomAlphaOfLengthBetween(1, 20), items);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<Calendar> instanceReader() {
|
||||
return Calendar::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Calendar doParseInstance(XContentParser parser) throws IOException {
|
||||
return Calendar.PARSER.apply(parser, null).build();
|
||||
}
|
||||
|
||||
public void testNullId() {
|
||||
NullPointerException ex = expectThrows(NullPointerException.class, () -> new Calendar(null, Collections.emptyList()));
|
||||
assertEquals(Calendar.ID.getPreferredName() + " must not be null", ex.getMessage());
|
||||
}
|
||||
|
||||
public void testDocumentId() {
|
||||
assertThat(Calendar.documentId("foo"), equalTo("calendar_foo"));
|
||||
}
|
||||
}
|
|
@ -278,7 +278,8 @@ public class JobProviderIT extends XPackSingleNodeTestCase {
|
|||
for (SpecialEvent event : events) {
|
||||
IndexRequest indexRequest = new IndexRequest(MlMetaIndex.INDEX_NAME, MlMetaIndex.TYPE, event.documentId());
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
indexRequest.source(event.toXContent(builder, ToXContent.EMPTY_PARAMS));
|
||||
ToXContent.MapParams params = new ToXContent.MapParams(Collections.singletonMap(MlMetaIndex.INCLUDE_TYPE_KEY, "true"));
|
||||
indexRequest.source(event.toXContent(builder, params));
|
||||
bulkRequest.add(indexRequest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"xpack.ml.delete_calendar": {
|
||||
"methods": [ "DELETE" ],
|
||||
"url": {
|
||||
"path": "/_xpack/ml/calendars/{calendar_id}",
|
||||
"paths": [ "/_xpack/ml/calendars/{calendar_id}" ],
|
||||
"parts": {
|
||||
"calendar_id": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"description" : "The ID of the calendar to delete"
|
||||
}
|
||||
}
|
||||
},
|
||||
"body": null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"xpack.ml.get_calendars": {
|
||||
"methods": [ "GET" ],
|
||||
"url": {
|
||||
"path": "/_xpack/ml/calendars/{calendar_id}",
|
||||
"paths": [
|
||||
"/_xpack/ml/calendars",
|
||||
"/_xpack/ml/calendars/{calendar_id}"
|
||||
],
|
||||
"parts": {
|
||||
"calendar_id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the calendar to fetch"
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"from": {
|
||||
"type": "int",
|
||||
"description": "skips a number of calendars"
|
||||
},
|
||||
"size": {
|
||||
"type": "int",
|
||||
"description": "specifies a max number of calendars to get"
|
||||
}
|
||||
}
|
||||
},
|
||||
"body": null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"xpack.ml.put_calendar": {
|
||||
"methods": [ "PUT" ],
|
||||
"url": {
|
||||
"path": "/_xpack/ml/calendars/{calendar_id}",
|
||||
"paths": [ "/_xpack/ml/calendars/{calendar_id}" ],
|
||||
"parts": {
|
||||
"calendar_id": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The ID of the calendar to create"
|
||||
}
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"description" : "The calendar details",
|
||||
"required" : false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
"Test calendar CRUD":
|
||||
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "advent"
|
||||
body: >
|
||||
{
|
||||
"job_ids": ["abc", "xyz"]
|
||||
}
|
||||
- match: { calendar_id: advent }
|
||||
- match: { job_ids.0: abc }
|
||||
- match: { job_ids.1: xyz }
|
||||
|
||||
- do:
|
||||
xpack.ml.get_calendars:
|
||||
calendar_id: "advent"
|
||||
- match: { count: 1 }
|
||||
- match:
|
||||
calendars.0:
|
||||
calendar_id: "advent"
|
||||
job_ids: ["abc", "xyz"]
|
||||
- is_false: type
|
||||
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Dogs of the Year"
|
||||
body: >
|
||||
{
|
||||
"job_ids": ["abc2"]
|
||||
}
|
||||
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Cats of the Year"
|
||||
|
||||
- do:
|
||||
xpack.ml.get_calendars: {}
|
||||
- match: { count: 3 }
|
||||
|
||||
- do:
|
||||
xpack.ml.delete_calendar:
|
||||
calendar_id: "Dogs of the Year"
|
||||
|
||||
- do:
|
||||
xpack.ml.get_calendars: {}
|
||||
- match: { count: 2 }
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
xpack.ml.get_calendars:
|
||||
calendar_id: "Dogs of the Year"
|
||||
|
||||
---
|
||||
"Test PageParams":
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Calendar1"
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Calendar2"
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Calendar3"
|
||||
|
||||
- do:
|
||||
xpack.ml.get_calendars:
|
||||
from: 2
|
||||
- match: { count: 1 }
|
||||
- match: { calendars.0.calendar_id: Calendar3 }
|
||||
|
||||
- do:
|
||||
xpack.ml.get_calendars:
|
||||
from: 1
|
||||
size: 1
|
||||
- match: { count: 1 }
|
||||
- match: { calendars.0.calendar_id: Calendar2 }
|
||||
|
||||
---
|
||||
"Test PageParams with ID is invalid":
|
||||
- do:
|
||||
catch: bad_request
|
||||
xpack.ml.get_calendars:
|
||||
calendar_id: Tides
|
||||
size: 10
|
||||
|
||||
---
|
||||
"Test cannot overwrite an exisiting calendar":
|
||||
|
||||
- do:
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Mayan"
|
||||
body: >
|
||||
{
|
||||
"job_ids": ["apocalypse"]
|
||||
}
|
||||
|
||||
- do:
|
||||
catch: /version_conflict_engine_exception/
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "Mayan"
|
||||
|
||||
---
|
||||
"Test cannot create calendar with name _all":
|
||||
- do:
|
||||
catch: bad_request
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "_all"
|
|
@ -17,6 +17,8 @@ integTestRunner {
|
|||
systemProperty 'tests.rest.blacklist', [
|
||||
// Remove tests that are expected to throw an exception, because we cannot then
|
||||
// know whether to expect an authorization exception or a validation exception
|
||||
'ml/calendar_crud/Test cannot create calendar with name _all',
|
||||
'ml/calendar_crud/Test PageParams with ID is invalid',
|
||||
'ml/custom_all_field/Test querying custom all field',
|
||||
'ml/datafeeds_crud/Test delete datafeed with missing id',
|
||||
'ml/datafeeds_crud/Test put datafeed referring to missing job_id',
|
||||
|
|
Loading…
Reference in New Issue