Adjust validation endpoints (elastic/elasticsearch#812)
Changes are: 1. The detector validation endpoint is changed from /_xpack/ml/_validate/detector to /_xpack/ml/anomaly_detectors/_validate/detector 2. A new endpoint is added for validating an entire job config: /_xpack/ml/anomaly_detectors/_validate Relates elastic/elasticsearch#630 Original commit: elastic/x-pack-elasticsearch@7b2031e746
This commit is contained in:
parent
4eab74ce29
commit
ab957b6d91
|
@ -25,6 +25,8 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.settings.SettingsFilter;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.xpack.ml.action.ValidateJobConfigAction;
|
||||
import org.elasticsearch.xpack.ml.rest.validate.RestValidateJobConfigAction;
|
||||
import org.elasticsearch.xpack.persistent.RemovePersistentTaskAction;
|
||||
import org.elasticsearch.xpack.persistent.PersistentActionCoordinator;
|
||||
import org.elasticsearch.xpack.persistent.PersistentActionRegistry;
|
||||
|
@ -271,6 +273,7 @@ public class MlPlugin extends Plugin implements ActionPlugin {
|
|||
new RestCloseJobAction(settings, restController),
|
||||
new RestFlushJobAction(settings, restController),
|
||||
new RestValidateDetectorAction(settings, restController),
|
||||
new RestValidateJobConfigAction(settings, restController),
|
||||
new RestGetCategoriesAction(settings, restController),
|
||||
new RestGetModelSnapshotsAction(settings, restController),
|
||||
new RestRevertModelSnapshotAction(settings, restController),
|
||||
|
@ -309,6 +312,7 @@ public class MlPlugin extends Plugin implements ActionPlugin {
|
|||
new ActionHandler<>(CloseJobAction.INSTANCE, CloseJobAction.TransportAction.class),
|
||||
new ActionHandler<>(FlushJobAction.INSTANCE, FlushJobAction.TransportAction.class),
|
||||
new ActionHandler<>(ValidateDetectorAction.INSTANCE, ValidateDetectorAction.TransportAction.class),
|
||||
new ActionHandler<>(ValidateJobConfigAction.INSTANCE, ValidateJobConfigAction.TransportAction.class),
|
||||
new ActionHandler<>(GetCategoriesAction.INSTANCE, GetCategoriesAction.TransportAction.class),
|
||||
new ActionHandler<>(GetModelSnapshotsAction.INSTANCE, GetModelSnapshotsAction.TransportAction.class),
|
||||
new ActionHandler<>(RevertModelSnapshotAction.INSTANCE, RevertModelSnapshotAction.TransportAction.class),
|
||||
|
|
|
@ -34,7 +34,7 @@ public class ValidateDetectorAction
|
|||
extends Action<ValidateDetectorAction.Request, ValidateDetectorAction.Response, ValidateDetectorAction.RequestBuilder> {
|
||||
|
||||
public static final ValidateDetectorAction INSTANCE = new ValidateDetectorAction();
|
||||
public static final String NAME = "cluster:admin/ml/validate/detector";
|
||||
public static final String NAME = "cluster:admin/ml/anomaly_detectors/validate/detector";
|
||||
|
||||
protected ValidateDetectorAction() {
|
||||
super(NAME);
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
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.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ValidateJobConfigAction
|
||||
extends Action<ValidateJobConfigAction.Request, ValidateJobConfigAction.Response, ValidateJobConfigAction.RequestBuilder> {
|
||||
|
||||
public static final ValidateJobConfigAction INSTANCE = new ValidateJobConfigAction();
|
||||
public static final String NAME = "cluster:admin/ml/anomaly_detectors/validate";
|
||||
|
||||
protected ValidateJobConfigAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new RequestBuilder(client, INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response newResponse() {
|
||||
return new Response();
|
||||
}
|
||||
|
||||
public static class RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder> {
|
||||
|
||||
protected RequestBuilder(ElasticsearchClient client, ValidateJobConfigAction action) {
|
||||
super(client, action, new Request());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Request extends ActionRequest implements ToXContent {
|
||||
|
||||
private Job job;
|
||||
|
||||
public static Request parseRequest(XContentParser parser) {
|
||||
Job.Builder job = Job.PARSER.apply(parser, null);
|
||||
// When jobs are PUT their ID must be supplied in the URL - assume this will
|
||||
// be valid unless an invalid job ID is specified in the JSON to be validated
|
||||
return new Request(job.build(true, (job.getId() != null) ? job.getId() : "ok"));
|
||||
}
|
||||
|
||||
Request() {
|
||||
this.job = null;
|
||||
}
|
||||
|
||||
public Request(Job job) {
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
public Job getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
job.writeTo(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
job = new Job(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
job.toXContent(builder, params);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(job);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Request other = (Request) obj;
|
||||
return Objects.equals(job, other.job);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Response extends AcknowledgedResponse {
|
||||
|
||||
public Response() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Response(boolean acknowledged) {
|
||||
super(acknowledged);
|
||||
}
|
||||
|
||||
@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<Request, Response> {
|
||||
|
||||
@Inject
|
||||
public TransportAction(Settings settings, TransportService transportService, ClusterService clusterService, ThreadPool threadPool,
|
||||
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(settings, ValidateJobConfigAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver,
|
||||
Request::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(Request request, ActionListener<Response> listener) {
|
||||
listener.onResponse(new Response(true));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -71,6 +71,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContent
|
|||
|
||||
public static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>("job_details", Builder::new);
|
||||
|
||||
public static final int MAX_JOB_ID_LENGTH = 64;
|
||||
public static final long MIN_BACKGROUND_PERSIST_INTERVAL = 3600;
|
||||
|
||||
static {
|
||||
PARSER.declareString(Builder::setId, ID);
|
||||
PARSER.declareStringOrNull(Builder::setDescription, DESCRIPTION);
|
||||
|
@ -140,6 +143,30 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContent
|
|||
ModelDebugConfig modelDebugConfig, IgnoreDowntime ignoreDowntime,
|
||||
Long renormalizationWindowDays, Long backgroundPersistInterval, Long modelSnapshotRetentionDays, Long resultsRetentionDays,
|
||||
Map<String, Object> customSettings, String modelSnapshotId, String indexName) {
|
||||
|
||||
if (analysisConfig == null) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_MISSING_ANALYSISCONFIG));
|
||||
}
|
||||
|
||||
checkValueNotLessThan(0, "timeout", timeout);
|
||||
checkValueNotLessThan(0, "renormalizationWindowDays", renormalizationWindowDays);
|
||||
checkValueNotLessThan(MIN_BACKGROUND_PERSIST_INTERVAL, "backgroundPersistInterval", backgroundPersistInterval);
|
||||
checkValueNotLessThan(0, "modelSnapshotRetentionDays", modelSnapshotRetentionDays);
|
||||
checkValueNotLessThan(0, "resultsRetentionDays", resultsRetentionDays);
|
||||
|
||||
if (!MlStrings.isValidId(jobId)) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_ID, ID.getPreferredName(), jobId));
|
||||
}
|
||||
if (jobId.length() > MAX_JOB_ID_LENGTH) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_ID_TOO_LONG, MAX_JOB_ID_LENGTH));
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(indexName)) {
|
||||
indexName = jobId;
|
||||
} else if (!MlStrings.isValidId(indexName)) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_ID, INDEX_NAME.getPreferredName()));
|
||||
}
|
||||
|
||||
this.jobId = jobId;
|
||||
this.description = description;
|
||||
this.createTime = createTime;
|
||||
|
@ -496,10 +523,14 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContent
|
|||
}
|
||||
}
|
||||
|
||||
private static void checkValueNotLessThan(long minVal, String name, Long value) {
|
||||
if (value != null && value < minVal) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_FIELD_VALUE_TOO_LOW, name, minVal, value));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
public static final int MAX_JOB_ID_LENGTH = 64;
|
||||
public static final long MIN_BACKGROUND_PERSIST_INTERVAL = 3600;
|
||||
public static final long DEFAULT_TIMEOUT = 600;
|
||||
|
||||
private String id;
|
||||
|
@ -641,15 +672,6 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContent
|
|||
}
|
||||
|
||||
public Job build(boolean fromApi, String urlJobId) {
|
||||
if (analysisConfig == null) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_MISSING_ANALYSISCONFIG));
|
||||
}
|
||||
|
||||
checkValueNotLessThan(0, "timeout", timeout);
|
||||
checkValueNotLessThan(0, "renormalizationWindowDays", renormalizationWindowDays);
|
||||
checkValueNotLessThan(MIN_BACKGROUND_PERSIST_INTERVAL, "backgroundPersistInterval", backgroundPersistInterval);
|
||||
checkValueNotLessThan(0, "modelSnapshotRetentionDays", modelSnapshotRetentionDays);
|
||||
checkValueNotLessThan(0, "resultsRetentionDays", resultsRetentionDays);
|
||||
|
||||
Date createTime;
|
||||
Date finishedTime;
|
||||
|
@ -672,19 +694,6 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContent
|
|||
modelSnapshotId = this.modelSnapshotId;
|
||||
}
|
||||
|
||||
if (!MlStrings.isValidId(id)) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_ID, ID.getPreferredName(), id));
|
||||
}
|
||||
if (id.length() > MAX_JOB_ID_LENGTH) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_ID_TOO_LONG, MAX_JOB_ID_LENGTH));
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(indexName)) {
|
||||
indexName = id;
|
||||
} else if (!MlStrings.isValidId(indexName)) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_ID, INDEX_NAME.getPreferredName()));
|
||||
}
|
||||
|
||||
return new Job(
|
||||
id, description, createTime, finishedTime, lastDataTime, timeout, analysisConfig, analysisLimits,
|
||||
dataDescription, modelDebugConfig, ignoreDowntime, renormalizationWindowDays,
|
||||
|
@ -692,11 +701,5 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContent
|
|||
indexName
|
||||
);
|
||||
}
|
||||
|
||||
private static void checkValueNotLessThan(long minVal, String name, Long value) {
|
||||
if (value != null && value < minVal) {
|
||||
throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_FIELD_VALUE_TOO_LOW, name, minVal, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public class RestValidateDetectorAction extends BaseRestHandler {
|
|||
|
||||
public RestValidateDetectorAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(RestRequest.Method.POST, MlPlugin.BASE_PATH + "_validate/detector", this);
|
||||
controller.registerHandler(RestRequest.Method.POST, MlPlugin.BASE_PATH + "anomaly_detectors/_validate/detector", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.validate;
|
||||
|
||||
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.AcknowledgedRestListener;
|
||||
import org.elasticsearch.xpack.ml.MlPlugin;
|
||||
import org.elasticsearch.xpack.ml.action.ValidateJobConfigAction;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RestValidateJobConfigAction extends BaseRestHandler {
|
||||
|
||||
public RestValidateJobConfigAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(RestRequest.Method.POST, MlPlugin.BASE_PATH + "anomaly_detectors/_validate", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
|
||||
XContentParser parser = restRequest.contentOrSourceParamParser();
|
||||
ValidateJobConfigAction.Request validateConfigRequest = ValidateJobConfigAction.Request.parseRequest(parser);
|
||||
return channel ->
|
||||
client.execute(ValidateJobConfigAction.INSTANCE, validateConfigRequest, new AcknowledgedRestListener<>(channel));
|
||||
}
|
||||
|
||||
}
|
|
@ -31,8 +31,8 @@ public class GetJobsActionResponseTests extends AbstractStreamableTestCase<GetJo
|
|||
int listSize = randomInt(10);
|
||||
List<Job> jobList = new ArrayList<>(listSize);
|
||||
for (int j = 0; j < listSize; j++) {
|
||||
String jobId = randomAsciiOfLength(10);
|
||||
String description = randomBoolean() ? randomAsciiOfLength(10) : null;
|
||||
String jobId = "job" + j;
|
||||
String description = randomBoolean() ? randomAsciiOfLength(100) : null;
|
||||
Date createTime = new Date(randomNonNegativeLong());
|
||||
Date finishedTime = randomBoolean() ? new Date(randomNonNegativeLong()) : null;
|
||||
Date lastDataTime = randomBoolean() ? new Date(randomNonNegativeLong()) : null;
|
||||
|
@ -43,14 +43,14 @@ public class GetJobsActionResponseTests extends AbstractStreamableTestCase<GetJo
|
|||
DataDescription dataDescription = randomBoolean() ? new DataDescription.Builder().build() : null;
|
||||
ModelDebugConfig modelDebugConfig = randomBoolean() ? new ModelDebugConfig(randomDouble(), randomAsciiOfLength(10)) : null;
|
||||
IgnoreDowntime ignoreDowntime = randomFrom(IgnoreDowntime.values());
|
||||
Long normalizationWindowDays = randomBoolean() ? randomLong() : null;
|
||||
Long backgroundPersistInterval = randomBoolean() ? randomLong() : null;
|
||||
Long modelSnapshotRetentionDays = randomBoolean() ? randomLong() : null;
|
||||
Long resultsRetentionDays = randomBoolean() ? randomLong() : null;
|
||||
Long normalizationWindowDays = randomBoolean() ? Long.valueOf(randomIntBetween(0, 365)) : null;
|
||||
Long backgroundPersistInterval = randomBoolean() ? Long.valueOf(randomIntBetween(3600, 86400)) : null;
|
||||
Long modelSnapshotRetentionDays = randomBoolean() ? Long.valueOf(randomIntBetween(0, 365)) : null;
|
||||
Long resultsRetentionDays = randomBoolean() ? Long.valueOf(randomIntBetween(0, 365)) : null;
|
||||
Map<String, Object> customConfig = randomBoolean() ? Collections.singletonMap(randomAsciiOfLength(10), randomAsciiOfLength(10))
|
||||
: null;
|
||||
String modelSnapshotId = randomBoolean() ? randomAsciiOfLength(10) : null;
|
||||
String indexName = randomAsciiOfLength(10);
|
||||
String indexName = randomBoolean() ? "index" + j : null;
|
||||
Job job = new Job(jobId, description, createTime, finishedTime, lastDataTime,
|
||||
timeout, analysisConfig, analysisLimits, dataDescription,
|
||||
modelDebugConfig, ignoreDowntime, normalizationWindowDays, backgroundPersistInterval,
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.xpack.ml.action.ValidateJobConfigAction.Request;
|
||||
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
||||
import org.elasticsearch.xpack.ml.job.config.AnalysisLimits;
|
||||
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
||||
import org.elasticsearch.xpack.ml.job.config.Detector;
|
||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||
import org.elasticsearch.xpack.ml.support.AbstractStreamableXContentTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ValidateJobConfigActionRequestTests extends AbstractStreamableXContentTestCase<ValidateJobConfigAction.Request> {
|
||||
|
||||
@Override
|
||||
protected Request createTestInstance() {
|
||||
List<Detector> detectors = new ArrayList<>();
|
||||
detectors.add(new Detector.Builder(randomFrom(Detector.FIELD_NAME_FUNCTIONS), randomAsciiOfLengthBetween(1, 20)).build());
|
||||
detectors.add(new Detector.Builder(randomFrom(Detector.COUNT_WITHOUT_FIELD_FUNCTIONS), null).build());
|
||||
AnalysisConfig.Builder analysisConfigBuilder = new AnalysisConfig.Builder(detectors);
|
||||
analysisConfigBuilder.setBucketSpan(randomIntBetween(60, 86400));
|
||||
if (randomBoolean()) {
|
||||
analysisConfigBuilder.setLatency(randomIntBetween(0, 12));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
analysisConfigBuilder.setCategorizationFieldName(randomAsciiOfLengthBetween(1, 20));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
analysisConfigBuilder.setSummaryCountFieldName(randomAsciiOfLengthBetween(1, 20));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
List<String> influencers = new ArrayList<>();
|
||||
for (int i = randomIntBetween(1, 5); i > 0; --i) {
|
||||
influencers.add(randomAsciiOfLengthBetween(1, 20));
|
||||
}
|
||||
analysisConfigBuilder.setInfluencers(influencers);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
analysisConfigBuilder.setOverlappingBuckets(randomBoolean());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
analysisConfigBuilder.setMultivariateByFields(randomBoolean());
|
||||
}
|
||||
Job.Builder job = new Job.Builder("ok");
|
||||
job.setAnalysisConfig(analysisConfigBuilder);
|
||||
if (randomBoolean()) {
|
||||
DataDescription.Builder dataDescription = new DataDescription.Builder();
|
||||
if (randomBoolean()) {
|
||||
dataDescription.setFormat(DataDescription.DataFormat.DELIMITED);
|
||||
if (randomBoolean()) {
|
||||
dataDescription.setFieldDelimiter(';');
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
dataDescription.setQuoteCharacter('\'');
|
||||
}
|
||||
} else {
|
||||
dataDescription.setFormat(DataDescription.DataFormat.JSON);
|
||||
}
|
||||
dataDescription.setTimeField(randomAsciiOfLengthBetween(1, 20));
|
||||
if (randomBoolean()) {
|
||||
dataDescription.setTimeFormat("yyyy-MM-dd HH:mm:ssX");
|
||||
}
|
||||
job.setDataDescription(dataDescription);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
job.setAnalysisLimits(new AnalysisLimits(randomNonNegativeLong(), randomNonNegativeLong()));
|
||||
}
|
||||
return new Request(job.build(true, "ok"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Request createBlankInstance() {
|
||||
return new Request();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Request parseInstance(XContentParser parser) {
|
||||
return Request.parseRequest(parser);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"xpack.ml.validate": {
|
||||
"methods": [ "POST" ],
|
||||
"url": {
|
||||
"path": "/_xpack/ml/anomaly_detectors/_validate",
|
||||
"paths": [ "/_xpack/ml/anomaly_detectors/_validate" ],
|
||||
"params": {}
|
||||
},
|
||||
"body": {
|
||||
"description" : "The job config",
|
||||
"required" : true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
"xpack.ml.validate_detector": {
|
||||
"methods": [ "POST" ],
|
||||
"url": {
|
||||
"path": "/_xpack/ml/_validate/detector",
|
||||
"paths": [ "/_xpack/ml/_validate/detector" ],
|
||||
"path": "/_xpack/ml/anomaly_detectors/_validate/detector",
|
||||
"paths": [ "/_xpack/ml/anomaly_detectors/_validate/detector" ],
|
||||
"params": {}
|
||||
},
|
||||
"body": {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
"Test valid job config":
|
||||
- do:
|
||||
xpack.ml.validate:
|
||||
body: >
|
||||
{
|
||||
"analysis_config": {
|
||||
"bucket_span": 3600,
|
||||
"detectors": [{"function": "metric", "field_name": "responsetime", "by_field_name": "airline"}]
|
||||
},
|
||||
"data_description": {
|
||||
"format": "delimited",
|
||||
"field_delimiter": ",",
|
||||
"time_field": "time",
|
||||
"time_format": "yyyy-MM-dd HH:mm:ssX"
|
||||
}
|
||||
}
|
||||
- match: { acknowledged: true }
|
||||
|
||||
---
|
||||
"Test invalid job config":
|
||||
- do:
|
||||
catch: /.data_description. failed to parse field .format./
|
||||
xpack.ml.validate:
|
||||
body: >
|
||||
{
|
||||
"analysis_config": {
|
||||
"bucket_span": 3600,
|
||||
"detectors": [{"function": "metric", "field_name": "responsetime", "by_field_name": "airline"}]
|
||||
},
|
||||
"data_description": {
|
||||
"format": "wrong",
|
||||
"field_delimiter": ",",
|
||||
"time_field": "time",
|
||||
"time_format": "yyyy-MM-dd HH:mm:ssX"
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
"Test valid job config with job ID":
|
||||
- do:
|
||||
xpack.ml.validate:
|
||||
body: >
|
||||
{
|
||||
"job_id": "farequote",
|
||||
"analysis_config": {
|
||||
"bucket_span": 3600,
|
||||
"detectors": [{"function": "metric", "field_name": "responsetime", "by_field_name": "airline"}]
|
||||
},
|
||||
"data_description": {
|
||||
"format": "delimited",
|
||||
"field_delimiter": ",",
|
||||
"time_field": "time",
|
||||
"time_format": "yyyy-MM-dd HH:mm:ssX"
|
||||
}
|
||||
}
|
||||
- match: { acknowledged: true }
|
||||
|
||||
---
|
||||
"Test job config that's invalid only because of the job ID":
|
||||
- do:
|
||||
catch: /Invalid job_id; '_' must be lowercase alphanumeric, may contain hyphens or underscores, may not start with underscore/
|
||||
xpack.ml.validate:
|
||||
body: >
|
||||
{
|
||||
"job_id": "_",
|
||||
"analysis_config": {
|
||||
"bucket_span": 3600,
|
||||
"detectors": [{"function": "metric", "field_name": "responsetime", "by_field_name": "airline"}]
|
||||
},
|
||||
"data_description": {
|
||||
"format": "delimited",
|
||||
"field_delimiter": ",",
|
||||
"time_field": "time",
|
||||
"time_format": "yyyy-MM-dd HH:mm:ssX"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue