updated rule evealutor tests (#9711)

* updated rule evealutor tests

* Update - review comments
This commit is contained in:
Mohit Yadav 2023-01-13 14:43:55 +05:30 committed by GitHub
parent c3197beaf4
commit bbfb50f298
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 165 additions and 119 deletions

View File

@ -51,12 +51,12 @@ public class AlertsRuleEvaluator {
description = "Returns true if the change event entity being accessed has source as mentioned in condition", description = "Returns true if the change event entity being accessed has source as mentioned in condition",
examples = {"matchAnySource('bot', 'user')"}, examples = {"matchAnySource('bot', 'user')"},
paramInputType = READ_FROM_PARAM_CONTEXT) paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchAnySource(String... originEntity) { public boolean matchAnySource(String... originEntities) {
if (changeEvent == null) { if (changeEvent == null || changeEvent.getEntityType() == null) {
return false; return false;
} }
String changeEventEntity = changeEvent.getEntityType(); String changeEventEntity = changeEvent.getEntityType();
for (String entityType : originEntity) { for (String entityType : originEntities) {
if (changeEventEntity.equals(entityType)) { if (changeEventEntity.equals(entityType)) {
return true; return true;
} }
@ -74,14 +74,7 @@ public class AlertsRuleEvaluator {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getEntity() == null) {
return false; return false;
} }
Class<? extends EntityInterface> entityClass = Entity.getEntityClassFromType(changeEvent.getEntityType()); EntityInterface entity = getEntity(changeEvent);
EntityInterface entity;
if (changeEvent.getEntity() instanceof String) {
entity = JsonUtils.readValue((String) changeEvent.getEntity(), entityClass);
} else {
entity = JsonUtils.convertValue(changeEvent.getEntity(), entityClass);
}
EntityReference ownerReference = entity.getOwner(); EntityReference ownerReference = entity.getOwner();
if (ownerReference != null) { if (ownerReference != null) {
if (USER.equals(ownerReference.getType())) { if (USER.equals(ownerReference.getType())) {
@ -109,11 +102,11 @@ public class AlertsRuleEvaluator {
description = "Returns true if the change event entity being accessed has following entityName from the List.", description = "Returns true if the change event entity being accessed has following entityName from the List.",
examples = {"matchAnyEntityFqn('Name1', 'Name')"}, examples = {"matchAnyEntityFqn('Name1', 'Name')"},
paramInputType = ALL_INDEX_ELASTIC_SEARCH) paramInputType = ALL_INDEX_ELASTIC_SEARCH)
public boolean matchAnyEntityFqn(String... entityNames) { public boolean matchAnyEntityFqn(String... entityNames) throws IOException {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getEntity() == null) {
return false; return false;
} }
EntityInterface entity = (EntityInterface) changeEvent.getEntity(); EntityInterface entity = getEntity(changeEvent);
for (String name : entityNames) { for (String name : entityNames) {
if (entity.getFullyQualifiedName().equals(name)) { if (entity.getFullyQualifiedName().equals(name)) {
return true; return true;
@ -128,11 +121,11 @@ public class AlertsRuleEvaluator {
description = "Returns true if the change event entity being accessed has following entityId from the List.", description = "Returns true if the change event entity being accessed has following entityId from the List.",
examples = {"matchAnyEntityId('uuid1', 'uuid2')"}, examples = {"matchAnyEntityId('uuid1', 'uuid2')"},
paramInputType = ALL_INDEX_ELASTIC_SEARCH) paramInputType = ALL_INDEX_ELASTIC_SEARCH)
public boolean matchAnyEntityId(String... entityIds) { public boolean matchAnyEntityId(String... entityIds) throws IOException {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getEntity() == null) {
return false; return false;
} }
EntityInterface entity = (EntityInterface) changeEvent.getEntity(); EntityInterface entity = getEntity(changeEvent);
for (String id : entityIds) { for (String id : entityIds) {
if (entity.getId().equals(UUID.fromString(id))) { if (entity.getId().equals(UUID.fromString(id))) {
return true; return true;
@ -148,7 +141,7 @@ public class AlertsRuleEvaluator {
examples = {"matchAnyEventType('entityCreated', 'entityUpdated', 'entityDeleted', 'entitySoftDeleted')"}, examples = {"matchAnyEventType('entityCreated', 'entityUpdated', 'entityDeleted', 'entitySoftDeleted')"},
paramInputType = READ_FROM_PARAM_CONTEXT) paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchAnyEventType(String... eventTypesList) { public boolean matchAnyEventType(String... eventTypesList) {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getEventType() == null) {
return false; return false;
} }
String eventType = changeEvent.getEventType().toString(); String eventType = changeEvent.getEventType().toString();
@ -167,7 +160,7 @@ public class AlertsRuleEvaluator {
examples = {"matchTestResult('Success', 'Failed', 'Aborted')"}, examples = {"matchTestResult('Success', 'Failed', 'Aborted')"},
paramInputType = READ_FROM_PARAM_CONTEXT) paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchTestResult(String... testResults) { public boolean matchTestResult(String... testResults) {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getChangeDescription() == null) {
return false; return false;
} }
if (!changeEvent.getEntityType().equals(TEST_CASE)) { if (!changeEvent.getEntityType().equals(TEST_CASE)) {
@ -195,7 +188,7 @@ public class AlertsRuleEvaluator {
examples = {"matchUpdatedBy('user1', 'user2')"}, examples = {"matchUpdatedBy('user1', 'user2')"},
paramInputType = READ_FROM_PARAM_CONTEXT) paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchUpdatedBy(String... updatedByUserList) { public boolean matchUpdatedBy(String... updatedByUserList) {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getUserName() == null) {
return false; return false;
} }
String entityUpdatedBy = changeEvent.getUserName(); String entityUpdatedBy = changeEvent.getUserName();
@ -214,7 +207,7 @@ public class AlertsRuleEvaluator {
examples = {"matchAnyFieldChange('fieldName1', 'fieldName')"}, examples = {"matchAnyFieldChange('fieldName1', 'fieldName')"},
paramInputType = NOT_REQUIRED) paramInputType = NOT_REQUIRED)
public boolean matchAnyFieldChange(String... fieldChangeUpdate) { public boolean matchAnyFieldChange(String... fieldChangeUpdate) {
if (changeEvent == null || changeEvent.getEntity() == null) { if (changeEvent == null || changeEvent.getChangeDescription() == null) {
return false; return false;
} }
Set<String> fields = ChangeEventParser.getUpdatedField(changeEvent); Set<String> fields = ChangeEventParser.getUpdatedField(changeEvent);
@ -225,4 +218,15 @@ public class AlertsRuleEvaluator {
} }
return false; return false;
} }
private EntityInterface getEntity(ChangeEvent event) throws IOException {
Class<? extends EntityInterface> entityClass = Entity.getEntityClassFromType(event.getEntityType());
EntityInterface entity;
if (event.getEntity() instanceof String) {
entity = JsonUtils.readValue((String) event.getEntity(), entityClass);
} else {
entity = JsonUtils.convertValue(event.getEntity(), entityClass);
}
return entity;
}
} }

View File

@ -3,138 +3,180 @@ package org.openmetadata.service.alerts;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openmetadata.service.security.policyevaluator.CompiledRule.parseExpression; import static org.openmetadata.service.security.policyevaluator.CompiledRule.parseExpression;
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
import java.util.ArrayList; import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito; import org.junit.jupiter.api.TestInfo;
import org.openmetadata.schema.api.data.CreateTable;
import org.openmetadata.schema.entity.data.Table; import org.openmetadata.schema.entity.data.Table;
import org.openmetadata.schema.entity.teams.Team; import org.openmetadata.schema.tests.type.TestCaseResult;
import org.openmetadata.schema.entity.teams.User; import org.openmetadata.schema.tests.type.TestCaseStatus;
import org.openmetadata.schema.type.ChangeDescription;
import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.Column;
import org.openmetadata.schema.type.TagLabel; import org.openmetadata.schema.type.ColumnDataType;
import org.openmetadata.schema.type.EventType;
import org.openmetadata.schema.type.FieldChange;
import org.openmetadata.service.Entity; import org.openmetadata.service.Entity;
import org.openmetadata.service.jdbi3.CollectionDAO.TeamDAO; import org.openmetadata.service.OpenMetadataApplicationTest;
import org.openmetadata.service.jdbi3.CollectionDAO.UserDAO; import org.openmetadata.service.resources.EntityResourceTest;
import org.openmetadata.service.jdbi3.TableRepository; import org.openmetadata.service.resources.databases.TableResourceTest;
import org.openmetadata.service.jdbi3.TeamRepository;
import org.openmetadata.service.jdbi3.UserRepository;
import org.openmetadata.service.security.policyevaluator.ResourceContext;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
class AlertsRuleEvaluatorTest { class AlertsRuleEvaluatorTest extends OpenMetadataApplicationTest {
private static Table table; private static TableResourceTest tableResourceTest;
private static User user;
private static EvaluationContext evaluationContext;
@BeforeAll @BeforeAll
public static void setup() { public static void setup(TestInfo test) throws URISyntaxException, IOException {
Entity.registerEntity(User.class, Entity.USER, Mockito.mock(UserDAO.class), Mockito.mock(UserRepository.class)); tableResourceTest = new TableResourceTest();
Entity.registerEntity(Team.class, Entity.TEAM, Mockito.mock(TeamDAO.class), Mockito.mock(TeamRepository.class)); tableResourceTest.setup(test);
table = new Table().withName("table");
user = new User().withId(UUID.randomUUID()).withName("user");
ResourceContext resourceContext =
ResourceContext.builder()
.resource("table")
.entity(table)
.entityRepository(Mockito.mock(TableRepository.class))
.build();
// SubjectContext subjectContext = new SubjectContext(user);
// RuleEvaluator ruleEvaluator = new RuleEvaluator(null, subjectContext, resourceContext);
ChangeEvent event = new ChangeEvent();
// evaluationContext = new StandardEvaluationContext(ruleEvaluator);
} }
@Test @Test
void test_noOwner() { void test_matchAnySource() {
// Set no owner to the entity and test noOwner method // Create a change Event with Entity Type and test for source in list and not in list
table.setOwner(null); ChangeEvent changeEvent = new ChangeEvent();
assertTrue(evaluateExpression("noOwner()")); changeEvent.setEntityType("alert");
assertFalse(evaluateExpression("!noOwner()")); AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
// Set owner to the entity and test noOwner method assertTrue(evaluateExpression("matchAnySource('alert')", evaluationContext));
table.setOwner(new EntityReference().withId(UUID.randomUUID()).withType(Entity.USER)); assertFalse(evaluateExpression("matchAnySource('bot')", evaluationContext));
assertFalse(evaluateExpression("noOwner()"));
assertTrue(evaluateExpression("!noOwner()"));
} }
@Test @Test
void test_isOwner() { void test_matchAnyOwnerName(TestInfo test) throws IOException {
// Table owner is a different user (random ID) and hence isOwner returns false // Create Table Entity
table.setOwner(new EntityReference().withId(UUID.randomUUID()).withType(Entity.USER).withName("otherUser")); List<Column> columns = List.of(TableResourceTest.getColumn("c1", ColumnDataType.INT, null));
assertFalse(evaluateExpression("isOwner()")); CreateTable create =
assertTrue(evaluateExpression("!isOwner()")); tableResourceTest.createRequest(test).withColumns(columns).withOwner(EntityResourceTest.USER1_REF);
Table createdTable = tableResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
// Table owner is same as the user in subjectContext and hence isOwner returns true // Create a change Event with the Entity Table
table.setOwner(new EntityReference().withId(user.getId()).withType(Entity.USER).withName(user.getName())); ChangeEvent changeEvent = new ChangeEvent();
assertTrue(evaluateExpression("isOwner()")); changeEvent.setEntityType(Entity.TABLE);
assertFalse(evaluateExpression("!isOwner()")); changeEvent.setEntity(createdTable);
// noOwner() || isOwner() - with noOwner being true and isOwner false // Test Owner Name Present in list and not present in list
table.setOwner(null); AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
assertTrue(evaluateExpression("noOwner() || isOwner()")); EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertFalse(evaluateExpression("!noOwner() && !isOwner()")); assertTrue(
evaluateExpression("matchAnyOwnerName('" + EntityResourceTest.USER1.getName() + "')", evaluationContext));
// noOwner() || isOwner() - with noOwner is false and isOwner true assertFalse(evaluateExpression("matchAnyOwnerName('tempName')", evaluationContext));
table.setOwner(new EntityReference().withId(user.getId()).withType(Entity.USER).withName(user.getName()));
assertTrue(evaluateExpression("noOwner() || isOwner()"));
assertFalse(evaluateExpression("!noOwner() && !isOwner()"));
} }
@Test @Test
void test_matchAllTags() { void test_matchAnyEntityFqn(TestInfo test) throws IOException {
table.withTags(getTags("tag1", "tag2", "tag3")); // Create Table Entity
List<Column> columns = List.of(TableResourceTest.getColumn("c1", ColumnDataType.INT, null));
CreateTable create = tableResourceTest.createRequest(test).withColumns(columns);
Table createdTable = tableResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
// All tags present // Create a change Event with the Entity Table
assertTrue(evaluateExpression("matchAllTags('tag1', 'tag2', 'tag3')")); ChangeEvent changeEvent = new ChangeEvent();
assertFalse(evaluateExpression("!matchAllTags('tag1', 'tag2', 'tag3')")); changeEvent.setEntityType(Entity.TABLE);
assertTrue(evaluateExpression("matchAllTags('tag1', 'tag2')")); changeEvent.setEntity(createdTable);
assertFalse(evaluateExpression("!matchAllTags('tag1', 'tag2')"));
assertTrue(evaluateExpression("matchAllTags('tag1')"));
assertFalse(evaluateExpression("!matchAllTags('tag1')"));
// Tag 'tag4' is missing // Test Entity Fqn in List of match and not present in list
assertFalse(evaluateExpression("matchAllTags('tag1', 'tag2', 'tag3', 'tag4')")); AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
assertTrue(evaluateExpression("!matchAllTags('tag1', 'tag2', 'tag3', 'tag4')")); EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertFalse(evaluateExpression("matchAllTags('tag1', 'tag2', 'tag4')")); String fqn = createdTable.getFullyQualifiedName();
assertTrue(evaluateExpression("!matchAllTags('tag1', 'tag2', 'tag4')")); assertTrue(evaluateExpression("matchAnyEntityFqn('" + fqn + "')", evaluationContext));
assertFalse(evaluateExpression("matchAllTags('tag2', 'tag4')")); assertFalse(evaluateExpression("matchAnyEntityFqn('testFQN1')", evaluationContext));
assertTrue(evaluateExpression("!matchAllTags('tag2', 'tag4')"));
assertFalse(evaluateExpression("matchAllTags('tag4')"));
assertTrue(evaluateExpression("!matchAllTags('tag4')"));
} }
@Test @Test
void test_matchAnyTag() { void test_matchAnyEntityId(TestInfo test) throws IOException {
table.withTags(getTags("tag1", "tag2", "tag3")); // Create Table Entity
List<Column> columns = List.of(TableResourceTest.getColumn("c1", ColumnDataType.INT, null));
CreateTable create = tableResourceTest.createRequest(test).withColumns(columns);
Table createdTable = tableResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
// Tag is present // Create a change Event with Table Entity and Type
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag2', 'tag3', 'tag4')")); ChangeEvent changeEvent = new ChangeEvent();
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag2', 'tag3', 'tag4')")); changeEvent.setEntityType(Entity.TABLE);
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag2', 'tag4')")); changeEvent.setEntity(createdTable);
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag2', 'tag4')"));
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag2', 'tag4')"));
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag2', 'tag4')"));
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag4')"));
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag4')"));
// Tag `tag4` is not present // Test Entity Id in List of match and not present in list
assertFalse(evaluateExpression("matchAnyTag('tag4')")); AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
assertTrue(evaluateExpression("!matchAnyTag('tag4')")); EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
String id = createdTable.getId().toString();
assertTrue(evaluateExpression("matchAnyEntityId('" + id + "')", evaluationContext));
assertFalse(evaluateExpression("matchAnyEntityId('" + UUID.randomUUID() + "')", evaluationContext));
} }
private Boolean evaluateExpression(String condition) { @Test
void test_matchAnyEventType() {
// Create a change Event with EventType
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEventType(EventType.ENTITY_CREATED);
// Check if eventType present in list or absent from the list
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchAnyEventType('entityCreated')", evaluationContext));
assertFalse(evaluateExpression("matchAnyEventType('entityUpdated')", evaluationContext));
}
@Test
void test_matchTestResult() {
// Create a change Description with Test Result Field
ChangeDescription changeDescription = new ChangeDescription();
changeDescription.setFieldsUpdated(
List.of(
new FieldChange()
.withName("testCaseResult")
.withOldValue("test1")
.withNewValue(new TestCaseResult().withTestCaseStatus(TestCaseStatus.Success))));
// Create a change event with Test Case and Test Result Change Description
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEntityType(Entity.TEST_CASE);
changeEvent.setChangeDescription(changeDescription);
// Test If Test Result status matches in list and if status not matches
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchTestResult('Success')", evaluationContext));
assertFalse(evaluateExpression("matchTestResult('Failed')", evaluationContext));
}
@Test
void test_matchUpdatedBy() {
// Create a change Event with updatedBy username
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setUserName("test");
// Test if the username is in list or not
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchUpdatedBy('test')", evaluationContext));
assertFalse(evaluateExpression("matchUpdatedBy('test1')", evaluationContext));
}
@Test
void test_matchAnyFieldChange() {
// Create a change Event with some Change Description and Field Change
ChangeDescription changeDescription = new ChangeDescription();
changeDescription.setFieldsUpdated(
List.of(new FieldChange().withName("test").withOldValue("test1").withNewValue("test2")));
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setChangeDescription(changeDescription);
// Test if the updated field matches from the list or not
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchAnyFieldChange('test')", evaluationContext));
assertFalse(evaluateExpression("matchAnyFieldChange('temp')", evaluationContext));
}
private Boolean evaluateExpression(String condition, EvaluationContext evaluationContext) {
return parseExpression(condition).getValue(evaluationContext, Boolean.class); return parseExpression(condition).getValue(evaluationContext, Boolean.class);
} }
private List<TagLabel> getTags(String... tags) {
List<TagLabel> tagLabels = new ArrayList<>();
for (String tag : tags) {
tagLabels.add(new TagLabel().withTagFQN(tag));
}
return tagLabels;
}
} }