From d8fcc0807769f46120b3d3ec92b5745656a116b7 Mon Sep 17 00:00:00 2001 From: Sarat Date: Wed, 21 Oct 2020 21:22:38 -0700 Subject: [PATCH 1/3] Adding new Search Info API --- build.gradle | 1 + .../ad/AnomalyDetectorPlugin.java | 10 +- .../RestSearchAnomalyDetectorInfoAction.java | 79 ++++++++++++ .../SearchAnomalyDetectorInfoAction.java | 28 +++++ .../SearchAnomalyDetectorInfoRequest.java | 61 +++++++++ .../SearchAnomalyDetectorInfoResponse.java | 65 ++++++++++ ...rchAnomalyDetectorInfoTransportAction.java | 117 ++++++++++++++++++ .../ad/util/RestHandlerUtils.java | 2 + .../ad/AnomalyDetectorRestTestCase.java | 24 ++++ .../ad/rest/AnomalyDetectorRestApiIT.java | 24 ++++ .../SearchAnomalyDetectorInfoActionTests.java | 115 +++++++++++++++++ 11 files changed, 524 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/amazon/opendistroforelasticsearch/ad/rest/RestSearchAnomalyDetectorInfoAction.java create mode 100644 src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java create mode 100644 src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoRequest.java create mode 100644 src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoResponse.java create mode 100644 src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java diff --git a/build.gradle b/build.gradle index d2aa92b7..8f63172c 100644 --- a/build.gradle +++ b/build.gradle @@ -259,6 +259,7 @@ List jacocoExclusions = [ 'com.amazon.opendistroforelasticsearch.ad.transport.GetAnomalyDetectorResponse', 'com.amazon.opendistroforelasticsearch.ad.transport.IndexAnomalyDetectorRequest', 'com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyResultTransportAction*', + 'com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorInfoTransportAction*', // TODO: hc caused coverage to drop //'com.amazon.opendistroforelasticsearch.ad.ml.ModelManager', diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorPlugin.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorPlugin.java index a9dbcf0a..42a3f015 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorPlugin.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorPlugin.java @@ -99,6 +99,7 @@ import com.amazon.opendistroforelasticsearch.ad.rest.RestGetAnomalyDetectorAction; import com.amazon.opendistroforelasticsearch.ad.rest.RestIndexAnomalyDetectorAction; import com.amazon.opendistroforelasticsearch.ad.rest.RestSearchAnomalyDetectorAction; +import com.amazon.opendistroforelasticsearch.ad.rest.RestSearchAnomalyDetectorInfoAction; import com.amazon.opendistroforelasticsearch.ad.rest.RestSearchAnomalyResultAction; import com.amazon.opendistroforelasticsearch.ad.rest.RestStatsAnomalyDetectorAction; import com.amazon.opendistroforelasticsearch.ad.settings.AnomalyDetectorSettings; @@ -139,6 +140,8 @@ import com.amazon.opendistroforelasticsearch.ad.transport.RCFResultAction; import com.amazon.opendistroforelasticsearch.ad.transport.RCFResultTransportAction; import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorAction; +import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorInfoAction; +import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorInfoTransportAction; import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorTransportAction; import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyResultAction; import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyResultTransportAction; @@ -241,6 +244,7 @@ public List getRestHandlers( ); RestStatsAnomalyDetectorAction statsAnomalyDetectorAction = new RestStatsAnomalyDetectorAction(adStats, this.nodeFilter); RestAnomalyDetectorJobAction anomalyDetectorJobAction = new RestAnomalyDetectorJobAction(settings, clusterService); + RestSearchAnomalyDetectorInfoAction searchAnomalyDetectorInfoAction = new RestSearchAnomalyDetectorInfoAction(); return ImmutableList .of( @@ -251,7 +255,8 @@ public List getRestHandlers( deleteAnomalyDetectorAction, executeAnomalyDetectorAction, anomalyDetectorJobAction, - statsAnomalyDetectorAction + statsAnomalyDetectorAction, + searchAnomalyDetectorInfoAction ); } @@ -621,7 +626,8 @@ public List getNamedXContent() { new ActionHandler<>(AnomalyDetectorJobAction.INSTANCE, AnomalyDetectorJobTransportAction.class), new ActionHandler<>(ADResultBulkAction.INSTANCE, ADResultBulkTransportAction.class), new ActionHandler<>(EntityResultAction.INSTANCE, EntityResultTransportAction.class), - new ActionHandler<>(EntityProfileAction.INSTANCE, EntityProfileTransportAction.class) + new ActionHandler<>(EntityProfileAction.INSTANCE, EntityProfileTransportAction.class), + new ActionHandler<>(SearchAnomalyDetectorInfoAction.INSTANCE, SearchAnomalyDetectorInfoTransportAction.class) ); } diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/rest/RestSearchAnomalyDetectorInfoAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/rest/RestSearchAnomalyDetectorInfoAction.java new file mode 100644 index 00000000..b57395b7 --- /dev/null +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/rest/RestSearchAnomalyDetectorInfoAction.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.ad.rest; + +import static com.amazon.opendistroforelasticsearch.ad.util.RestHandlerUtils.COUNT; +import static com.amazon.opendistroforelasticsearch.ad.util.RestHandlerUtils.MATCH; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestHandler; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.action.RestToXContentListener; + +import com.amazon.opendistroforelasticsearch.ad.AnomalyDetectorPlugin; +import com.amazon.opendistroforelasticsearch.ad.constant.CommonErrorMessages; +import com.amazon.opendistroforelasticsearch.ad.settings.EnabledSetting; +import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorInfoAction; +import com.amazon.opendistroforelasticsearch.ad.transport.SearchAnomalyDetectorInfoRequest; +import com.google.common.collect.ImmutableList; + +public class RestSearchAnomalyDetectorInfoAction extends BaseRestHandler { + + public static final String SEARCH_ANOMALY_DETECTOR_INFO_ACTION = "search_anomaly_detector_info"; + + private static final Logger logger = LogManager.getLogger(RestSearchAnomalyDetectorInfoAction.class); + + public RestSearchAnomalyDetectorInfoAction() {} + + @Override + public String getName() { + return SEARCH_ANOMALY_DETECTOR_INFO_ACTION; + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, org.elasticsearch.client.node.NodeClient client) throws IOException { + if (!EnabledSetting.isADPluginEnabled()) { + throw new IllegalStateException(CommonErrorMessages.DISABLED_ERR_MSG); + } + + String detectorName = request.param("name", null); + String rawPath = request.rawPath(); + + SearchAnomalyDetectorInfoRequest searchAnomalyDetectorInfoRequest = new SearchAnomalyDetectorInfoRequest(detectorName, rawPath); + return channel -> client + .execute(SearchAnomalyDetectorInfoAction.INSTANCE, searchAnomalyDetectorInfoRequest, new RestToXContentListener<>(channel)); + } + + @Override + public List routes() { + return ImmutableList + .of( + // get the count of number of detectors + new RestHandler.Route( + RestRequest.Method.GET, + String.format(Locale.ROOT, "%s/%s", AnomalyDetectorPlugin.AD_BASE_DETECTORS_URI, COUNT) + ), + // get if a detector name exists with name + new RestHandler.Route(RestRequest.Method.GET, String.format("%s/%s", AnomalyDetectorPlugin.AD_BASE_DETECTORS_URI, MATCH)) + ); + } +} diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java new file mode 100644 index 00000000..464dfabd --- /dev/null +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.ad.transport; + +import org.elasticsearch.action.ActionType; + +public class SearchAnomalyDetectorInfoAction extends ActionType { + public static final SearchAnomalyDetectorInfoAction INSTANCE = new SearchAnomalyDetectorInfoAction(); + public static final String NAME = "cluster:admin/opendistro/ad/detector/info"; + + private SearchAnomalyDetectorInfoAction() { + super(NAME, SearchAnomalyDetectorInfoResponse::new); + } + +} diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoRequest.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoRequest.java new file mode 100644 index 00000000..0f5ec878 --- /dev/null +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoRequest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.ad.transport; + +import java.io.IOException; + +import org.elasticsearch.action.ActionRequest; +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; + +public class SearchAnomalyDetectorInfoRequest extends ActionRequest { + + private String name; + private String rawPath; + + public SearchAnomalyDetectorInfoRequest(StreamInput in) throws IOException { + super(in); + name = in.readOptionalString(); + rawPath = in.readString(); + } + + public SearchAnomalyDetectorInfoRequest(String name, String rawPath) throws IOException { + super(); + this.name = name; + this.rawPath = rawPath; + } + + public String getName() { + return name; + } + + public String getRawPath() { + return rawPath; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeOptionalString(name); + out.writeString(rawPath); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoResponse.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoResponse.java new file mode 100644 index 00000000..67792a00 --- /dev/null +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoResponse.java @@ -0,0 +1,65 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.ad.transport; + +import java.io.IOException; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import com.amazon.opendistroforelasticsearch.ad.util.RestHandlerUtils; + +public class SearchAnomalyDetectorInfoResponse extends ActionResponse implements ToXContentObject { + private long count; + private boolean nameExists; + + public SearchAnomalyDetectorInfoResponse(StreamInput in) throws IOException { + super(in); + count = in.readLong(); + nameExists = in.readBoolean(); + } + + public SearchAnomalyDetectorInfoResponse(long count, boolean nameExists) { + this.count = count; + this.nameExists = nameExists; + } + + public long getCount() { + return count; + } + + public boolean isNameExists() { + return nameExists; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeLong(count); + out.writeBoolean(nameExists); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(RestHandlerUtils.COUNT, count); + builder.field(RestHandlerUtils.MATCH, nameExists); + builder.endObject(); + return builder; + } +} diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java new file mode 100644 index 00000000..de7b1332 --- /dev/null +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java @@ -0,0 +1,117 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.ad.transport; + +import static com.amazon.opendistroforelasticsearch.ad.model.AnomalyDetector.ANOMALY_DETECTORS_INDEX; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.ActionListener; +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.cluster.service.ClusterService; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.TermsQueryBuilder; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.transport.TransportService; + +import com.amazon.opendistroforelasticsearch.ad.util.RestHandlerUtils; + +public class SearchAnomalyDetectorInfoTransportAction extends + HandledTransportAction { + private static final Logger LOG = LogManager.getLogger(SearchAnomalyDetectorInfoTransportAction.class); + private final Client client; + private final ClusterService clusterService; + + @Inject + public SearchAnomalyDetectorInfoTransportAction( + TransportService transportService, + ActionFilters actionFilters, + Client client, + ClusterService clusterService + ) { + super(SearchAnomalyDetectorInfoAction.NAME, transportService, actionFilters, SearchAnomalyDetectorInfoRequest::new); + this.client = client; + this.clusterService = clusterService; + } + + @Override + protected void doExecute( + Task task, + SearchAnomalyDetectorInfoRequest request, + ActionListener listener + ) { + String name = request.getName(); + String rawPath = request.getRawPath(); + try (ThreadContext.StoredContext context = client.threadPool().getThreadContext().stashContext()) { + SearchRequest searchRequest = new SearchRequest().indices(ANOMALY_DETECTORS_INDEX); + if (rawPath.endsWith(RestHandlerUtils.COUNT)) { + // Count detectors + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchRequest.source(searchSourceBuilder); + client.search(searchRequest, new ActionListener() { + + @Override + public void onResponse(SearchResponse searchResponse) { + SearchAnomalyDetectorInfoResponse response = new SearchAnomalyDetectorInfoResponse( + searchResponse.getHits().getTotalHits().value, + false + ); + listener.onResponse(response); + } + + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + }); + } else { + // Match name with existing detectors + TermsQueryBuilder query = QueryBuilders.termsQuery("name.keyword", name); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(query); + searchRequest.source(searchSourceBuilder); + client.search(searchRequest, new ActionListener() { + + @Override + public void onResponse(SearchResponse searchResponse) { + boolean nameExists = false; + if (searchResponse.getHits().getTotalHits().value > 0) { + nameExists = true; + } else { + nameExists = false; + } + SearchAnomalyDetectorInfoResponse response = new SearchAnomalyDetectorInfoResponse(0, nameExists); + listener.onResponse(response); + } + + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } + }); + } + } catch (Exception e) { + LOG.error(e); + listener.onFailure(e); + } + } +} diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/util/RestHandlerUtils.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/util/RestHandlerUtils.java index 9448bf7e..1097f65b 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/ad/util/RestHandlerUtils.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/util/RestHandlerUtils.java @@ -63,6 +63,8 @@ public final class RestHandlerUtils { public static final String PROFILE = "_profile"; public static final String TYPE = "type"; public static final String ENTITY = "entity"; + public static final String COUNT = "count"; + public static final String MATCH = "match"; public static final ToXContent.MapParams XCONTENT_WITH_TYPE = new ToXContent.MapParams(ImmutableMap.of("with_type", "true")); private static final String KIBANA_USER_AGENT = "Kibana"; diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java index 19242fef..c3b502b1 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java @@ -239,4 +239,28 @@ public Response getDetectorProfile(String detectorId) throws IOException { public Response getDetectorProfile(String detectorId, boolean all) throws IOException { return getDetectorProfile(detectorId, all, ""); } + + public Response getSearchDetectorCount() throws IOException { + return TestHelpers + .makeRequest( + client(), + "GET", + TestHelpers.AD_BASE_DETECTORS_URI + "/" + RestHandlerUtils.COUNT, + null, + "", + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) + ); + } + + public Response getSearchDetectorMatch(String name) throws IOException { + return TestHelpers + .makeRequest( + client(), + "GET", + TestHelpers.AD_BASE_DETECTORS_URI + "/" + RestHandlerUtils.MATCH, + ImmutableMap.of("name",name), + "", + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) + ); + } } diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java index 811cdbdc..a0b13b1d 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java @@ -1040,4 +1040,28 @@ public void testCustomizedProfileAnomalyDetector() throws Exception { Response profileResponse = getDetectorProfile(detector.getDetectorId(), true, "/models/"); assertEquals("Incorrect profile status", RestStatus.OK, restStatus(profileResponse)); } + + public void testSearchAnomalyDetectorCount() throws Exception { + AnomalyDetector detector = createRandomAnomalyDetector(true, true); + Response countResponse = getSearchDetectorCount(); + Map responseMap = entityAsMap(countResponse); + Integer count = (Integer) responseMap.get("count"); + assertEquals((long) count, 1); + } + + public void testSearchAnomalyDetectorNoMatch() throws Exception { + AnomalyDetector detector = createRandomAnomalyDetector(true, true); + Response matchResponse = getSearchDetectorMatch(detector.getName()); + Map responseMap = entityAsMap(matchResponse); + boolean nameExists = (boolean) responseMap.get("match"); + assertEquals(nameExists, true); + } + + public void testSearchAnomalyDetectorMatch() throws Exception { + AnomalyDetector detector = createRandomAnomalyDetector(true, true); + Response matchResponse = getSearchDetectorMatch(detector.getName() + "newDetector"); + Map responseMap = entityAsMap(matchResponse); + boolean nameExists = (boolean) responseMap.get("match"); + assertEquals(nameExists, false); + } } diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java new file mode 100644 index 00000000..dcf3f0a4 --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.ad.transport; + +import static org.mockito.Mockito.mock; + +import java.io.IOException; + +import org.apache.lucene.index.IndexNotFoundException; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.transport.TransportService; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.amazon.opendistroforelasticsearch.ad.TestHelpers; + +public class SearchAnomalyDetectorInfoActionTests extends ESIntegTestCase { + private SearchAnomalyDetectorInfoRequest request; + private ActionListener response; + private SearchAnomalyDetectorInfoTransportAction action; + private Task task; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + action = new SearchAnomalyDetectorInfoTransportAction( + mock(TransportService.class), + mock(ActionFilters.class), + client(), + clusterService() + ); + task = mock(Task.class); + response = new ActionListener() { + @Override + public void onResponse(SearchAnomalyDetectorInfoResponse response) { + Assert.assertTrue(true); + } + + @Override + public void onFailure(Exception e) { + Assert.assertFalse(IndexNotFoundException.class == e.getClass()); + } + }; + } + + @Test + public void testSearchCount() throws IOException { + // Anomaly Detectors index will not exist, onFailure will be called + SearchAnomalyDetectorInfoRequest request = new SearchAnomalyDetectorInfoRequest(null, "count"); + action.doExecute(task, request, response); + } + + @Test + public void testSearchMatch() throws IOException { + // Anomaly Detectors index will not exist, onFailure will be called + SearchAnomalyDetectorInfoRequest request = new SearchAnomalyDetectorInfoRequest("testDetector", "match"); + action.doExecute(task, request, response); + } + + @Test + public void testSearchCountWithIndex() throws IOException { + + } + + @Test + public void testSearchInfoAction() { + Assert.assertNotNull(SearchAnomalyDetectorInfoAction.INSTANCE.name()); + Assert.assertEquals(SearchAnomalyDetectorInfoAction.INSTANCE.name(), SearchAnomalyDetectorInfoAction.NAME); + } + + @Test + public void testSearchInfoRequest() throws IOException { + SearchAnomalyDetectorInfoRequest request = new SearchAnomalyDetectorInfoRequest("testDetector", "match"); + BytesStreamOutput out = new BytesStreamOutput(); + request.writeTo(out); + StreamInput input = out.bytes().streamInput(); + SearchAnomalyDetectorInfoRequest newRequest = new SearchAnomalyDetectorInfoRequest(input); + Assert.assertEquals(request.getName(), newRequest.getName()); + Assert.assertEquals(request.getRawPath(), newRequest.getRawPath()); + Assert.assertNull(newRequest.validate()); + } + + @Test + public void testSearchInfoResponse() throws IOException { + SearchAnomalyDetectorInfoResponse response = new SearchAnomalyDetectorInfoResponse(1, true); + BytesStreamOutput out = new BytesStreamOutput(); + response.writeTo(out); + StreamInput input = out.bytes().streamInput(); + SearchAnomalyDetectorInfoResponse newResponse = new SearchAnomalyDetectorInfoResponse(input); + Assert.assertEquals(response.getCount(), newResponse.getCount()); + Assert.assertEquals(response.isNameExists(), newResponse.isNameExists()); + Assert.assertNotNull(response.toXContent(TestHelpers.builder(), ToXContent.EMPTY_PARAMS)); + } +} From 5703091e27b329bd983d469aab0785efcbd6b44c Mon Sep 17 00:00:00 2001 From: Sarat Date: Thu, 22 Oct 2020 00:06:29 -0700 Subject: [PATCH 2/3] Adding Spotless changes --- .../ad/AnomalyDetectorRestTestCase.java | 32 +++++++++---------- .../SearchAnomalyDetectorInfoActionTests.java | 5 --- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java index c3b502b1..1c196887 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/AnomalyDetectorRestTestCase.java @@ -242,25 +242,25 @@ public Response getDetectorProfile(String detectorId, boolean all) throws IOExce public Response getSearchDetectorCount() throws IOException { return TestHelpers - .makeRequest( - client(), - "GET", - TestHelpers.AD_BASE_DETECTORS_URI + "/" + RestHandlerUtils.COUNT, - null, - "", - ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) - ); + .makeRequest( + client(), + "GET", + TestHelpers.AD_BASE_DETECTORS_URI + "/" + RestHandlerUtils.COUNT, + null, + "", + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) + ); } public Response getSearchDetectorMatch(String name) throws IOException { return TestHelpers - .makeRequest( - client(), - "GET", - TestHelpers.AD_BASE_DETECTORS_URI + "/" + RestHandlerUtils.MATCH, - ImmutableMap.of("name",name), - "", - ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) - ); + .makeRequest( + client(), + "GET", + TestHelpers.AD_BASE_DETECTORS_URI + "/" + RestHandlerUtils.MATCH, + ImmutableMap.of("name", name), + "", + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "Kibana")) + ); } } diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java index dcf3f0a4..dfd69d77 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java @@ -78,11 +78,6 @@ public void testSearchMatch() throws IOException { action.doExecute(task, request, response); } - @Test - public void testSearchCountWithIndex() throws IOException { - - } - @Test public void testSearchInfoAction() { Assert.assertNotNull(SearchAnomalyDetectorInfoAction.INSTANCE.name()); From 9ce23c7fe3c6e61a5d3ae554bd38a8aade4621c1 Mon Sep 17 00:00:00 2001 From: Sarat Date: Thu, 22 Oct 2020 16:07:08 -0700 Subject: [PATCH 3/3] Rebasing changes and addressing comments --- .../SearchAnomalyDetectorInfoAction.java | 5 +++- ...rchAnomalyDetectorInfoTransportAction.java | 25 +++++++++++++------ .../ad/rest/AnomalyDetectorRestApiIT.java | 14 +++++++++++ .../SearchAnomalyDetectorInfoActionTests.java | 10 ++++---- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java index 464dfabd..73a6db4f 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoAction.java @@ -17,9 +17,12 @@ import org.elasticsearch.action.ActionType; +import com.amazon.opendistroforelasticsearch.ad.constant.CommonValue; + public class SearchAnomalyDetectorInfoAction extends ActionType { + // External Action which used for public facing RestAPIs. + public static final String NAME = CommonValue.EXTERNAL_ACTION_PREFIX + "detector/info"; public static final SearchAnomalyDetectorInfoAction INSTANCE = new SearchAnomalyDetectorInfoAction(); - public static final String NAME = "cluster:admin/opendistro/ad/detector/info"; private SearchAnomalyDetectorInfoAction() { super(NAME, SearchAnomalyDetectorInfoResponse::new); diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java index de7b1332..304acfbb 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoTransportAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -81,7 +82,14 @@ public void onResponse(SearchResponse searchResponse) { @Override public void onFailure(Exception e) { - listener.onFailure(e); + if (e.getClass() == IndexNotFoundException.class) { + // Anomaly Detectors index does not exist + // Could be that user is creating first detector + SearchAnomalyDetectorInfoResponse response = new SearchAnomalyDetectorInfoResponse(0, false); + listener.onResponse(response); + } else { + listener.onFailure(e); + } } }); } else { @@ -94,18 +102,21 @@ public void onFailure(Exception e) { @Override public void onResponse(SearchResponse searchResponse) { boolean nameExists = false; - if (searchResponse.getHits().getTotalHits().value > 0) { - nameExists = true; - } else { - nameExists = false; - } + nameExists = searchResponse.getHits().getTotalHits().value > 0; SearchAnomalyDetectorInfoResponse response = new SearchAnomalyDetectorInfoResponse(0, nameExists); listener.onResponse(response); } @Override public void onFailure(Exception e) { - listener.onFailure(e); + if (e.getClass() == IndexNotFoundException.class) { + // Anomaly Detectors index does not exist + // Could be that user is creating first detector + SearchAnomalyDetectorInfoResponse response = new SearchAnomalyDetectorInfoResponse(0, false); + listener.onResponse(response); + } else { + listener.onFailure(e); + } } }); } diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java index a0b13b1d..f4975315 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/rest/AnomalyDetectorRestApiIT.java @@ -1041,6 +1041,13 @@ public void testCustomizedProfileAnomalyDetector() throws Exception { assertEquals("Incorrect profile status", RestStatus.OK, restStatus(profileResponse)); } + public void testSearchAnomalyDetectorCountNoIndex() throws Exception { + Response countResponse = getSearchDetectorCount(); + Map responseMap = entityAsMap(countResponse); + Integer count = (Integer) responseMap.get("count"); + assertEquals((long) count, 0); + } + public void testSearchAnomalyDetectorCount() throws Exception { AnomalyDetector detector = createRandomAnomalyDetector(true, true); Response countResponse = getSearchDetectorCount(); @@ -1049,6 +1056,13 @@ public void testSearchAnomalyDetectorCount() throws Exception { assertEquals((long) count, 1); } + public void testSearchAnomalyDetectorMatchNoIndex() throws Exception { + Response matchResponse = getSearchDetectorMatch("name"); + Map responseMap = entityAsMap(matchResponse); + boolean nameExists = (boolean) responseMap.get("match"); + assertEquals(nameExists, false); + } + public void testSearchAnomalyDetectorNoMatch() throws Exception { AnomalyDetector detector = createRandomAnomalyDetector(true, true); Response matchResponse = getSearchDetectorMatch(detector.getName()); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java index dfd69d77..21a85df2 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/ad/transport/SearchAnomalyDetectorInfoActionTests.java @@ -19,7 +19,6 @@ import java.io.IOException; -import org.apache.lucene.index.IndexNotFoundException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -54,26 +53,27 @@ public void setUp() throws Exception { response = new ActionListener() { @Override public void onResponse(SearchAnomalyDetectorInfoResponse response) { - Assert.assertTrue(true); + Assert.assertEquals(response.getCount(), 0); + Assert.assertEquals(response.isNameExists(), false); } @Override public void onFailure(Exception e) { - Assert.assertFalse(IndexNotFoundException.class == e.getClass()); + Assert.assertTrue(true); } }; } @Test public void testSearchCount() throws IOException { - // Anomaly Detectors index will not exist, onFailure will be called + // Anomaly Detectors index will not exist, onResponse will be called SearchAnomalyDetectorInfoRequest request = new SearchAnomalyDetectorInfoRequest(null, "count"); action.doExecute(task, request, response); } @Test public void testSearchMatch() throws IOException { - // Anomaly Detectors index will not exist, onFailure will be called + // Anomaly Detectors index will not exist, onResponse will be called SearchAnomalyDetectorInfoRequest request = new SearchAnomalyDetectorInfoRequest("testDetector", "match"); action.doExecute(task, request, response); }