Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature/extensions] Validate Detector Action Handle request #742

Merged
merged 9 commits into from
Dec 5, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ public AnomalyDetectorExtension() {

@Override
public List<ExtensionRestHandler> getExtensionRestHandlers() {
return List.of(new RestCreateDetectorAction(extensionsRunner, this), new RestGetDetectorAction(), new RestValidateDetectorAction());
return List
.of(
new RestCreateDetectorAction(extensionsRunner, this),
new RestGetDetectorAction(),
new RestValidateDetectorAction(extensionsRunner, this)
);
}

@Override
Expand Down
117 changes: 97 additions & 20 deletions src/main/java/org/opensearch/ad/rest/RestValidateDetectorAction.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,121 @@
package org.opensearch.ad.rest;

import static org.opensearch.ad.util.RestHandlerUtils.TYPE;
import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.opensearch.rest.RestRequest.Method.POST;
import static org.opensearch.rest.RestStatus.NOT_FOUND;
import static org.opensearch.rest.RestStatus.OK;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.ad.AnomalyDetectorExtension;
import org.opensearch.ad.common.exception.ADValidationException;
import org.opensearch.ad.constant.CommonErrorMessages;
import org.opensearch.ad.model.AnomalyDetector;
import org.opensearch.ad.model.DetectorValidationIssue;
import org.opensearch.ad.model.ValidationAspect;
import org.opensearch.ad.settings.EnabledSetting;
import org.opensearch.ad.transport.ValidateAnomalyDetectorRequest;
import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.common.xcontent.NamedXContentRegistry;
import org.opensearch.common.xcontent.XContentBuilder;
import org.opensearch.common.xcontent.XContentParser;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.extensions.rest.ExtensionRestRequest;
import org.opensearch.extensions.rest.ExtensionRestResponse;
import org.opensearch.rest.RestHandler.Route;
import org.opensearch.rest.RestRequest.Method;
import org.opensearch.sdk.ExtensionRestHandler;
import org.opensearch.rest.RestStatus;
import org.opensearch.sdk.BaseExtensionRestHandler;
import org.opensearch.sdk.ExtensionsRunner;
import org.opensearch.sdk.RouteHandler;

public class RestValidateDetectorAction implements ExtensionRestHandler {
public class RestValidateDetectorAction extends BaseExtensionRestHandler {
private final Logger logger = LogManager.getLogger(RestValidateDetectorAction.class);
private final OpenSearchClient sdkClient;
private final NamedXContentRegistry xContentRegistry;

@Override
public List<Route> routes() {
return List.of(new Route(POST, "/detectors/_validate"), new Route(POST, "/detectors/_validate/{type}"));
public static final Set<String> ALL_VALIDATION_ASPECTS_STRS = Arrays
.asList(ValidationAspect.values())
.stream()
.map(aspect -> aspect.getName())
.collect(Collectors.toSet());

public RestValidateDetectorAction(ExtensionsRunner runner, AnomalyDetectorExtension extension) {
this.xContentRegistry = runner.getNamedXContentRegistry().getRegistry();
this.sdkClient = extension.getClient();
}

@Override
public ExtensionRestResponse handleRequest(ExtensionRestRequest request) {
protected List<RouteHandler> routeHandlers() {
return List.of(new RouteHandler(POST, "/detectors/_validate", (r) -> handleValidateDetectorRequest(r)));
}

private ExtensionRestResponse handleValidateDetectorRequest(ExtensionRestRequest request) {
if (!EnabledSetting.isADPluginEnabled()) {
throw new IllegalStateException(CommonErrorMessages.DISABLED_ERR_MSG);
}
Method method = request.method();

if (!Method.POST.equals(method)) {
return new ExtensionRestResponse(
request,
NOT_FOUND,
"Extension REST action improperly configured to handle " + request.toString()
);
owaiskazi19 marked this conversation as resolved.
Show resolved Hide resolved
AnomalyDetector detector;
XContentParser parser;
XContentBuilder builder = null;
ValidateAnomalyDetectorRequest validateAnomalyDetectorRequest;
try {
parser = request.contentParser(this.xContentRegistry);
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
String typesStr = validateTypeString(request);
DetectorValidationIssue issue = null;
try {
detector = AnomalyDetector.parse(parser);
// validateAnomalyDetectorRequest= new ValidateAnomalyDetectorRequest(
// detector,
// typesStr,
// extension,
// maxMultiEntityDetectors,
// maxAnomalyFeatures,
// requestTimeout
// );
} catch (Exception e) {
if (e instanceof ADValidationException) {
ADValidationException ADException = (ADValidationException) e;
issue = new DetectorValidationIssue(ADException.getAspect(), ADException.getType(), ADException.getMessage());
}
}

try {
builder = XContentBuilder.builder(XContentType.JSON.xContent());
builder.startObject();
builder.field("issue", issue);
owaiskazi19 marked this conversation as resolved.
Show resolved Hide resolved
builder.endObject();
} catch (IOException e) {
e.printStackTrace();
}

} catch (Exception e) {
return new ExtensionRestResponse(request, RestStatus.BAD_REQUEST, builder);
}
// do things with request
return new ExtensionRestResponse(request, OK, "placeholder");
return new ExtensionRestResponse(request, RestStatus.OK, "placeholder");
dbwiddis marked this conversation as resolved.
Show resolved Hide resolved
}

private Boolean validationTypesAreAccepted(String validationType) {
Set<String> typesInRequest = new HashSet<>(Arrays.asList(validationType.split(",")));
return (!Collections.disjoint(typesInRequest, ALL_VALIDATION_ASPECTS_STRS));
}

private String validateTypeString(ExtensionRestRequest request) {
String typesStr = request.param(TYPE);

// if type param isn't blank and isn't a part of possible validation types throws exception
if (!StringUtils.isBlank(typesStr)) {
if (!validationTypesAreAccepted(typesStr)) {
throw new IllegalStateException(CommonErrorMessages.NOT_EXISTENT_VALIDATION_TYPE);
}
}
return typesStr;
}

}
43 changes: 43 additions & 0 deletions src/test/java/org/opensearch/ad/model/ModelProfileTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.opensearch.ad.model;

import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder;

import java.io.IOException;

import org.opensearch.ad.AbstractADTest;
import org.opensearch.ad.constant.CommonName;
import org.opensearch.common.Strings;
import org.opensearch.common.xcontent.ToXContent;
import org.opensearch.common.xcontent.XContentBuilder;

import test.org.opensearch.ad.util.JsonDeserializer;

public class ModelProfileTests extends AbstractADTest {

public void testToXContent() throws IOException {
ModelProfile profile1 = new ModelProfile(
randomAlphaOfLength(5),
Entity.createSingleAttributeEntity(randomAlphaOfLength(5), randomAlphaOfLength(5)),
0
);
XContentBuilder builder = jsonBuilder();
builder.startObject();
profile1.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.endObject();
String json = Strings.toString(builder);
assertTrue(JsonDeserializer.hasChildNode(json, CommonName.ENTITY_KEY));
assertFalse(JsonDeserializer.hasChildNode(json, CommonName.MODEL_SIZE_IN_BYTES));

ModelProfile profile2 = new ModelProfile(randomAlphaOfLength(5), null, 1);

builder = jsonBuilder();
builder.startObject();
profile2.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.endObject();
json = Strings.toString(builder);

assertFalse(JsonDeserializer.hasChildNode(json, CommonName.ENTITY_KEY));
assertTrue(JsonDeserializer.hasChildNode(json, CommonName.MODEL_SIZE_IN_BYTES));

}
}