diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AbstractAlertPublisher.java b/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AbstractAlertPublisher.java index ad93f6fb6e3..20502fb493b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AbstractAlertPublisher.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AbstractAlertPublisher.java @@ -1,7 +1,5 @@ package org.openmetadata.service.alerts; -import static org.openmetadata.service.security.policyevaluator.CompiledRule.parseExpression; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -10,7 +8,6 @@ import java.util.concurrent.ConcurrentHashMap; import lombok.extern.slf4j.Slf4j; import org.openmetadata.schema.entity.alerts.Alert; import org.openmetadata.schema.entity.alerts.AlertAction; -import org.openmetadata.schema.entity.alerts.AlertFilterRule; import org.openmetadata.schema.entity.alerts.TriggerConfig; import org.openmetadata.schema.filter.EventFilter; import org.openmetadata.schema.filter.Filters; @@ -21,8 +18,6 @@ import org.openmetadata.service.events.EventPublisher; import org.openmetadata.service.events.errors.RetriableException; import org.openmetadata.service.resources.events.EventResource.ChangeEventList; import org.openmetadata.service.util.FilterUtil; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.support.StandardEvaluationContext; @Slf4j public abstract class AbstractAlertPublisher implements EventPublisher { @@ -74,7 +69,7 @@ public abstract class AbstractAlertPublisher implements EventPublisher { } // Evaluate ChangeEvent Alert Filtering - if (!evaluateAlertConditions(changeEvent)) { + if (!AlertUtil.evaluateAlertConditions(changeEvent, alert.getFilteringRules())) { return; } @@ -120,20 +115,4 @@ public abstract class AbstractAlertPublisher implements EventPublisher { return filter.isEmpty() || FilterUtil.shouldProcessRequest(changeEvent, filter); } } - - private boolean evaluateAlertConditions(ChangeEvent changeEvent) { - boolean result = false; - for (AlertFilterRule rule : alert.getFilteringRules()) { - AlertsRuleEvaluator ruleEvaluator = new AlertsRuleEvaluator(changeEvent); - StandardEvaluationContext evaluationContext = new StandardEvaluationContext(ruleEvaluator); - Expression expression = parseExpression(rule.getCondition()); - if (rule.getEffect() == AlertFilterRule.Effect.ALLOW) { - result = Boolean.TRUE.equals(expression.getValue(evaluationContext, Boolean.class)); - } else if (rule.getEffect() == AlertFilterRule.Effect.DENY) { - result = Boolean.FALSE.equals(expression.getValue(evaluationContext, Boolean.class)); - } - LOG.debug("Alert evaluated as Result : {}", result); - } - return result; - } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/alerts/ActivityFeedAlertCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/alerts/ActivityFeedAlertCache.java new file mode 100644 index 00000000000..56bbab9d075 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/alerts/ActivityFeedAlertCache.java @@ -0,0 +1,58 @@ +package org.openmetadata.service.alerts; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.UncheckedExecutionException; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import javax.annotation.CheckForNull; +import lombok.extern.slf4j.Slf4j; +import org.openmetadata.schema.entity.alerts.Alert; +import org.openmetadata.service.exception.EntityNotFoundException; +import org.openmetadata.service.jdbi3.AlertRepository; +import org.openmetadata.service.jdbi3.CollectionDAO; + +@Slf4j +public class ActivityFeedAlertCache { + private static final ActivityFeedAlertCache INSTANCE = new ActivityFeedAlertCache(); + private static volatile boolean INITIALIZED = false; + protected static LoadingCache ALERTS_CACHE; + protected static AlertRepository ALERT_REPOSITORY; + private static String activityFeedAlertName; + + public static void initialize(String alertName, CollectionDAO dao) { + if (!INITIALIZED) { + ALERTS_CACHE = + CacheBuilder.newBuilder() + .maximumSize(1000) + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(new ActivityFeedAlertCache.ActivityFeedAlertLoader()); + ALERT_REPOSITORY = new AlertRepository(dao); + INITIALIZED = true; + activityFeedAlertName = alertName; + } + } + + public static ActivityFeedAlertCache getInstance() { + return INSTANCE; + } + + public Alert getActivityFeedAlert() throws EntityNotFoundException { + try { + return ALERTS_CACHE.get(activityFeedAlertName); + } catch (ExecutionException | UncheckedExecutionException ex) { + throw new EntityNotFoundException(ex.getMessage()); + } + } + + static class ActivityFeedAlertLoader extends CacheLoader { + @Override + public Alert load(@CheckForNull String alertName) throws IOException { + Alert alert = ALERT_REPOSITORY.getByName(null, alertName, ALERT_REPOSITORY.getFields("*")); + LOG.debug("Loaded Alert {}", alert); + return alert; + } + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AlertUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AlertUtil.java index f083537c98d..2969344250c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AlertUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/alerts/AlertUtil.java @@ -14,7 +14,9 @@ import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.openmetadata.schema.entity.alerts.Alert; import org.openmetadata.schema.entity.alerts.AlertAction; +import org.openmetadata.schema.entity.alerts.AlertFilterRule; import org.openmetadata.schema.tests.type.TestCaseStatus; +import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.EventType; import org.openmetadata.schema.type.Function; import org.openmetadata.schema.type.ParamAdditionalContext; @@ -28,6 +30,7 @@ import org.openmetadata.service.exception.CatalogExceptionMessage; import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.resources.CollectionRegistry; import org.springframework.expression.Expression; +import org.springframework.expression.spel.support.StandardEvaluationContext; @Slf4j public class AlertUtil { @@ -48,6 +51,8 @@ public class AlertUtil { case EMAIL: publisher = new EmailAlertPublisher(alert, alertAction, daoCollection); break; + case ACTIVITY_FEED: + throw new IllegalArgumentException("Cannot create Activity Feed as Publisher."); default: throw new IllegalArgumentException("Invalid Alert Action Specified."); } @@ -112,4 +117,20 @@ public class AlertUtil { } return indexesToSearch; } + + public static boolean evaluateAlertConditions(ChangeEvent changeEvent, List alertFilterRules) { + boolean result = false; + for (AlertFilterRule rule : alertFilterRules) { + AlertsRuleEvaluator ruleEvaluator = new AlertsRuleEvaluator(changeEvent); + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(ruleEvaluator); + Expression expression = parseExpression(rule.getCondition()); + if (rule.getEffect() == AlertFilterRule.Effect.ALLOW) { + result = Boolean.TRUE.equals(expression.getValue(evaluationContext, Boolean.class)); + } else if (rule.getEffect() == AlertFilterRule.Effect.DENY) { + result = Boolean.FALSE.equals(expression.getValue(evaluationContext, Boolean.class)); + } + LOG.debug("Alert evaluated as Result : {}", result); + } + return result; + } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/events/ChangeEventHandler.java b/openmetadata-service/src/main/java/org/openmetadata/service/events/ChangeEventHandler.java index 2f0f8444de0..868c2a67978 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/events/ChangeEventHandler.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/events/ChangeEventHandler.java @@ -38,7 +38,6 @@ import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.EventType; import org.openmetadata.service.Entity; import org.openmetadata.service.OpenMetadataApplicationConfig; -import org.openmetadata.service.filter.FilterRegistry; import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.jdbi3.FeedRepository; import org.openmetadata.service.resources.feeds.MessageParser.EntityLink; @@ -69,7 +68,7 @@ public class ChangeEventHandler implements EventHandler { String loggedInUserName = securityContext.getUserPrincipal().getName(); try { notificationHandler.processNotifications(responseContext); - ChangeEvent changeEvent = getChangeEvent(method, responseContext); + ChangeEvent changeEvent = getChangeEvent(loggedInUserName, method, responseContext); if (changeEvent == null) { return null; } @@ -92,7 +91,7 @@ public class ChangeEventHandler implements EventHandler { if (Entity.shouldDisplayEntityChangeOnFeed(changeEvent.getEntityType())) { // ignore usageSummary updates in the feed boolean filterEnabled; - filterEnabled = FilterUtil.shouldProcessRequest(changeEvent, FilterRegistry.getAllFilters()); + filterEnabled = FilterUtil.shouldProcessRequest(changeEvent); if (filterEnabled) { for (Thread thread : listOrEmpty(getThreads(responseContext, loggedInUserName))) { // Don't create a thread if there is no message @@ -126,7 +125,7 @@ public class ChangeEventHandler implements EventHandler { return null; } - public ChangeEvent getChangeEvent(String method, ContainerResponseContext responseContext) { + public ChangeEvent getChangeEvent(String updateBy, String method, ContainerResponseContext responseContext) { // GET operations don't produce change events if (method.equals("GET")) { return null; @@ -147,7 +146,7 @@ public class ChangeEventHandler implements EventHandler { EntityReference entityReference = entityInterface.getEntityReference(); String entityType = entityReference.getType(); String entityFQN = entityReference.getFullyQualifiedName(); - return getChangeEvent(EventType.ENTITY_CREATED, entityType, entityInterface) + return getChangeEvent(updateBy, EventType.ENTITY_CREATED, entityType, entityInterface) .withEntity(entityInterface) .withEntityFullyQualifiedName(entityFQN); } @@ -171,7 +170,7 @@ public class ChangeEventHandler implements EventHandler { eventType = ENTITY_SOFT_DELETED; } - return getChangeEvent(eventType, entityType, entityInterface) + return getChangeEvent(updateBy, eventType, entityType, entityInterface) .withPreviousVersion(entityInterface.getChangeDescription().getPreviousVersion()) .withEntity(entityInterface) .withEntityFullyQualifiedName(entityFQN); @@ -188,7 +187,7 @@ public class ChangeEventHandler implements EventHandler { EntityReference entityReference = entityInterface.getEntityReference(); String entityType = entityReference.getType(); String entityFQN = entityReference.getFullyQualifiedName(); - return getChangeEvent(ENTITY_DELETED, entityType, entityInterface) + return getChangeEvent(updateBy, ENTITY_DELETED, entityType, entityInterface) .withPreviousVersion(entityInterface.getVersion()) .withEntity(entityInterface) .withEntityFullyQualifiedName(entityFQN); @@ -196,12 +195,13 @@ public class ChangeEventHandler implements EventHandler { return null; } - private static ChangeEvent getChangeEvent(EventType eventType, String entityType, EntityInterface entityInterface) { + private static ChangeEvent getChangeEvent( + String updateBy, EventType eventType, String entityType, EntityInterface entityInterface) { return new ChangeEvent() .withEventType(eventType) .withEntityId(entityInterface.getId()) .withEntityType(entityType) - .withUserName(entityInterface.getUpdatedBy()) + .withUserName(updateBy) .withTimestamp(entityInterface.getUpdatedAt()) .withChangeDescription(entityInterface.getChangeDescription()) .withCurrentVersion(entityInterface.getVersion()); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/events/EventFilter.java b/openmetadata-service/src/main/java/org/openmetadata/service/events/EventFilter.java index d17ec3d8ab7..eb26e42c0a9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/events/EventFilter.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/events/EventFilter.java @@ -18,16 +18,19 @@ import java.util.concurrent.ForkJoinPool; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Provider; import lombok.extern.slf4j.Slf4j; import org.jdbi.v3.core.Jdbi; import org.openmetadata.service.OpenMetadataApplicationConfig; +import org.openmetadata.service.security.JwtFilter; import org.openmetadata.service.util.ParallelStreamUtil; @Slf4j @Provider public class EventFilter implements ContainerResponseFilter { private static final List AUDITABLE_METHODS = Arrays.asList("POST", "PUT", "PATCH", "DELETE"); + private static final int FORK_JOIN_POOL_PARALLELISM = 20; private final ForkJoinPool forkJoinPool; private final List eventHandlers; @@ -65,7 +68,11 @@ public class EventFilter implements ContainerResponseFilter { eventHandlers .parallelStream() .forEach( - eventHandler -> - ParallelStreamUtil.runAsync(() -> eventHandler.process(requestContext, responseContext), forkJoinPool)); + (eventHandler) -> { + UriInfo uriInfo = requestContext.getUriInfo(); + if (JwtFilter.EXCLUDED_ENDPOINTS.stream().noneMatch(endpoint -> uriInfo.getPath().contains(endpoint))) { + ParallelStreamUtil.runAsync(() -> eventHandler.process(requestContext, responseContext), forkJoinPool); + } + }); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/filter/FilterRegistry.java b/openmetadata-service/src/main/java/org/openmetadata/service/filter/FilterRegistry.java index 27a168163a5..b441a97377f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/filter/FilterRegistry.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/filter/FilterRegistry.java @@ -13,18 +13,15 @@ package org.openmetadata.service.filter; -import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.openmetadata.schema.entity.alerts.Alert; import org.openmetadata.schema.filter.EventFilter; import org.openmetadata.schema.filter.Filters; -import org.openmetadata.schema.settings.Settings; import org.openmetadata.schema.type.EventType; -import org.openmetadata.service.resources.settings.SettingsCache; -import org.openmetadata.service.util.FilterUtil; +import org.openmetadata.service.alerts.ActivityFeedAlertCache; public class FilterRegistry { private static final ConcurrentHashMap> FILTERS_MAP = new ConcurrentHashMap<>(); @@ -47,23 +44,9 @@ public class FilterRegistry { } } - public static List> listAllFilters() { - List> filterList = new ArrayList<>(); - FILTERS_MAP.forEach((key, value) -> filterList.add(value)); - return filterList; - } - - public static List listAllEntitiesHavingFilter() { - return List.copyOf(FILTERS_MAP.keySet()); - } - - public static Map getFilterForEntity(String key) { - return FILTERS_MAP.get(key); - } - - public static Map> getAllFilters() throws IOException { - Settings settings = SettingsCache.getInstance().getEventFilters(); - add(FilterUtil.getEventFilterFromSettings(settings)); + public static Map> getAllFilters() { + Alert alert = ActivityFeedAlertCache.getInstance().getActivityFeedAlert(); + add(alert.getTriggerConfig().getEventFilters()); return FILTERS_MAP; } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertActionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertActionResource.java index 75e3d1e62a0..17b5c40b30e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertActionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertActionResource.java @@ -23,6 +23,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import java.io.IOException; +import java.util.List; import java.util.UUID; import javax.json.JsonPatch; import javax.validation.Valid; @@ -45,16 +46,21 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import lombok.extern.slf4j.Slf4j; +import org.openmetadata.common.utils.CommonUtil; import org.openmetadata.schema.api.events.CreateAlertAction; import org.openmetadata.schema.entity.alerts.AlertAction; import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.Include; +import org.openmetadata.service.OpenMetadataApplicationConfig; import org.openmetadata.service.jdbi3.AlertActionRepository; import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.jdbi3.ListFilter; import org.openmetadata.service.resources.Collection; import org.openmetadata.service.resources.EntityResource; +import org.openmetadata.service.resources.policies.PolicyResource; import org.openmetadata.service.security.Authorizer; +import org.openmetadata.service.util.EntityUtil; +import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.ResultList; @Slf4j @@ -62,7 +68,7 @@ import org.openmetadata.service.util.ResultList; @Api(value = "Alerts collection", tags = "Alerts collection") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -@Collection(name = "alertAction", order = 7) // init befoe Alert Resource Init +@Collection(name = "alertAction", order = 7) // init before Alert Resource Init public class AlertActionResource extends EntityResource { public static final String COLLECTION_PATH = "v1/alertAction/"; @@ -81,6 +87,28 @@ public class AlertActionResource extends EntityResource jsonDataFiles = EntityUtil.getJsonDataResources(".*json/data/alerts/alertsActionData.json$"); + if (jsonDataFiles.size() != 1) { + LOG.warn("Invalid number of jsonDataFiles {}. Only one expected.", jsonDataFiles.size()); + return; + } + String jsonDataFile = jsonDataFiles.get(0); + try { + String json = CommonUtil.getResourceAsStream(PolicyResource.class.getClassLoader(), jsonDataFile); + // Assumes to have 1 entry currently + AlertAction alertActions = JsonUtils.readObjects(json, AlertAction.class).get(0); + dao.initializeEntity(alertActions); + } catch (Exception e) { + LOG.warn("Failed to initialize the resource descriptors from file {}", jsonDataFile, e); + } + } + @GET @Operation( operationId = "listAlertActions", diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertResource.java index bd66cb1798c..306d6c325e9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/alerts/AlertResource.java @@ -52,19 +52,24 @@ import lombok.extern.slf4j.Slf4j; import org.openmetadata.common.utils.CommonUtil; import org.openmetadata.schema.api.events.CreateAlert; import org.openmetadata.schema.entity.alerts.Alert; +import org.openmetadata.schema.entity.alerts.AlertAction; import org.openmetadata.schema.entity.alerts.AlertActionStatus; import org.openmetadata.schema.entity.alerts.EntitySpelFilters; import org.openmetadata.schema.entity.alerts.TriggerConfig; import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.Function; import org.openmetadata.schema.type.Include; +import org.openmetadata.service.Entity; import org.openmetadata.service.OpenMetadataApplicationConfig; +import org.openmetadata.service.alerts.ActivityFeedAlertCache; import org.openmetadata.service.alerts.AlertUtil; import org.openmetadata.service.jdbi3.AlertRepository; import org.openmetadata.service.jdbi3.CollectionDAO; +import org.openmetadata.service.jdbi3.EntityRepository; import org.openmetadata.service.jdbi3.ListFilter; import org.openmetadata.service.resources.Collection; import org.openmetadata.service.resources.EntityResource; +import org.openmetadata.service.resources.policies.PolicyResource; import org.openmetadata.service.resources.settings.SettingsResource; import org.openmetadata.service.security.Authorizer; import org.openmetadata.service.util.EntityUtil; @@ -79,12 +84,12 @@ import org.openmetadata.service.util.ResultList; @Collection(name = "alerts", order = 8) // init after alertAction Resource public class AlertResource extends EntityResource { public static final String COLLECTION_PATH = "v1/alerts/"; - private final CollectionDAO.AlertDAO alertDAO; + private final CollectionDAO daoCollection; private List bootStrappedFilters = new ArrayList<>(); - private Map entitySpelFiltersList = new HashMap<>(); + private final Map entitySpelFiltersList = new HashMap<>(); static final String FIELDS = "triggerConfig,filteringRules,alertActions"; - private void initDefaultTriggersSettings() throws IOException { + private void initAlerts() throws IOException { // Load Trigger File List triggerDataFiles = EntityUtil.getJsonDataResources(".*json/data/alerts/triggerData.json$"); if (triggerDataFiles.size() != 1) { @@ -109,13 +114,44 @@ public class AlertResource extends EntityResource { try { String json = CommonUtil.getResourceAsStream(getClass().getClassLoader(), filterDataFile); List filters = JsonUtils.readObjects(json, EntitySpelFilters.class); - filters.forEach( - (spelFilter) -> { - entitySpelFiltersList.put(spelFilter.getEntityType(), spelFilter); - }); + filters.forEach((spelFilter) -> entitySpelFiltersList.put(spelFilter.getEntityType(), spelFilter)); } catch (Exception e) { LOG.warn("Failed to initialize the {} from file {}", "filters", filterDataFile, e); } + + // Initialize Alert For ActivityFeed, this does not have any publisher since it is for internal system filtering + List alertFile = EntityUtil.getJsonDataResources(".*json/data/alerts/alertsData.json$"); + List alertActionFile = EntityUtil.getJsonDataResources(".*json/data/alerts/alertsActionData.json$"); + String alertDataFile = alertFile.get(0); + String alertActionDataFile = alertActionFile.get(0); + Alert activityFeedAlert = null; + try { + String actionJson = CommonUtil.getResourceAsStream(PolicyResource.class.getClassLoader(), alertActionDataFile); + // Assumes to have 1 entry currently + AlertAction alertActions = JsonUtils.readObjects(actionJson, AlertAction.class).get(0); + + String alertJson = CommonUtil.getResourceAsStream(getClass().getClassLoader(), alertDataFile); + activityFeedAlert = JsonUtils.readObjects(alertJson, Alert.class).get(0); + // populate alert actions + EntityRepository actionEntityRepository = Entity.getEntityRepository(Entity.ALERT_ACTION); + AlertAction action = + actionEntityRepository.getByName(null, alertActions.getName(), actionEntityRepository.getFields("id")); + activityFeedAlert.setAlertActions(List.of(action.getEntityReference())); + dao.initializeEntity(activityFeedAlert); + } catch (Exception e) { + LOG.warn("Failed to initialize the {} from file {}", "filters", alertDataFile, e); + } + + // Init Publishers + ActivityFeedAlertCache.initialize(activityFeedAlert.getName(), daoCollection); + // Create Publishers + List listAllAlerts = daoCollection.alertDAO().listAllAlerts(daoCollection.alertDAO().getTableName()); + List alertList = JsonUtils.readObjects(listAllAlerts, Alert.class); + for (Alert alert : alertList) { + if (alert.getName().equals(activityFeedAlert.getName())) { + dao.addAlertActionPublishers(alert); + } + } } @Override @@ -125,7 +161,7 @@ public class AlertResource extends EntityResource { public AlertResource(CollectionDAO dao, Authorizer authorizer) { super(Alert.class, new AlertRepository(dao), authorizer); - alertDAO = dao.alertDAO(); + daoCollection = dao; } public static class AlertList extends ResultList { @@ -137,12 +173,7 @@ public class AlertResource extends EntityResource { @Override public void initialize(OpenMetadataApplicationConfig config) { try { - List listAllAlerts = alertDAO.listAllAlerts(alertDAO.getTableName()); - List alertList = JsonUtils.readObjects(listAllAlerts, Alert.class); - for (Alert alert : alertList) { - dao.addAlertActionPublishers(alert); - } - initDefaultTriggersSettings(); + initAlerts(); } catch (Exception ex) { // Starting application should not fail LOG.warn("Exception during initialization", ex); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/FilterUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/FilterUtil.java index d91a129b303..4fa7b241c8c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/util/FilterUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/FilterUtil.java @@ -32,6 +32,9 @@ import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.EventType; import org.openmetadata.schema.type.FieldChange; import org.openmetadata.service.Entity; +import org.openmetadata.service.alerts.ActivityFeedAlertCache; +import org.openmetadata.service.alerts.AlertUtil; +import org.openmetadata.service.filter.FilterRegistry; @Slf4j public class FilterUtil { @@ -65,6 +68,19 @@ public class FilterUtil { return false; } + public static boolean shouldProcessRequest(ChangeEvent event) { + // Check Trigger Conditions + if (!shouldProcessRequest(event, FilterRegistry.getAllFilters())) { + return false; + } + // Check Spel Conditions + if (!AlertUtil.evaluateAlertConditions( + event, ActivityFeedAlertCache.getInstance().getActivityFeedAlert().getFilteringRules())) { + return false; + } + return true; + } + private static boolean handleTestCaseFilter(ChangeEvent changeEvent, Filters sf) { List fieldChanges = getAllFieldChange(changeEvent); for (FieldChange fieldChange : fieldChanges) { diff --git a/openmetadata-service/src/main/resources/json/data/alerts/alertsActionData.json b/openmetadata-service/src/main/resources/json/data/alerts/alertsActionData.json new file mode 100644 index 00000000000..1555912b945 --- /dev/null +++ b/openmetadata-service/src/main/resources/json/data/alerts/alertsActionData.json @@ -0,0 +1,12 @@ +[ + { + "name": "ActivityFeedAlertAction", + "enabled": "true", + "alertActionType": "ActivityFeed", + "alertActionConfig": { + "name": "ActivityFeedAlertAction", + "description": "Controls the Activity Displayed on the Feeds Page" + }, + "provider" : "system" + } +] \ No newline at end of file diff --git a/openmetadata-service/src/main/resources/json/data/alerts/alertsData.json b/openmetadata-service/src/main/resources/json/data/alerts/alertsData.json new file mode 100644 index 00000000000..e54dbedcc25 --- /dev/null +++ b/openmetadata-service/src/main/resources/json/data/alerts/alertsData.json @@ -0,0 +1,232 @@ +[ + { + "name": "ActivityFeedAlert", + "triggerConfig": { + "type": "EntitySpecific", + "eventFilters": [ + { + "entityType": "all", + "filters": [ + { + "eventType": "entityCreated", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entityUpdated", + "include": [ + "all" + ], + "exclude": ["usageSummary"] + }, + { + "eventType": "entityDeleted", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entitySoftDeleted", + "include": [ + "all" + ], + "exclude": [] + } + ] + }, + { + "entityType": "table", + "filters": [ + { + "eventType": "entityCreated", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entityUpdated", + "include": [ + "description", + "owner", + "tags", + "followers", + "tableConstraints", + "constraint" + ], + "exclude": [] + }, + { + "eventType": "entityDeleted", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entitySoftDeleted", + "include": [ + "all" + ], + "exclude": [] + } + ] + }, + { + "entityType": "dashboard", + "filters": [ + { + "eventType": "entityCreated", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entityUpdated", + "include": [ + "description", + "owner", + "tags", + "followers" + ], + "exclude": [] + }, + { + "eventType": "entityDeleted", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entitySoftDeleted", + "include": [ + "all" + ], + "exclude": [] + } + ] + }, + { + "entityType": "pipeline", + "filters": [ + { + "eventType": "entityCreated", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entityUpdated", + "include": [ + "description", + "owner", + "tags", + "followers" + ], + "exclude": [] + }, + { + "eventType": "entityDeleted", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entitySoftDeleted", + "include": [ + "all" + ], + "exclude": [] + } + ] + }, + { + "entityType": "mlmodel", + "filters": [ + { + "eventType": "entityCreated", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entityUpdated", + "include": [ + "description", + "owner", + "tags", + "followers" + ], + "exclude": [] + }, + { + "eventType": "entityDeleted", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entitySoftDeleted", + "include": [ + "all" + ], + "exclude": [] + } + ] + }, + { + "entityType": "testCase", + "filters": [ + { + "eventType": "entityCreated", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entityUpdated", + "include": [ + "testCaseResultSuccess", + "testCaseResultFailed", + "testCaseResultAborted" + ], + "exclude": [] + }, + { + "eventType": "entityDeleted", + "include": [ + "all" + ], + "exclude": [] + }, + { + "eventType": "entitySoftDeleted", + "include": [ + "all" + ], + "exclude": [] + } + ] + } + ] + }, + "filteringRules": [ + { + "name": "DisableIngestionActivityFeedData", + "effect": "allow", + "condition": "!matchUpdatedBy('ingestion-bot')" + } + ], + "provider" : "system" + } +] \ No newline at end of file diff --git a/openmetadata-spec/src/main/resources/json/schema/alerts/alertAction.json b/openmetadata-spec/src/main/resources/json/schema/alerts/alertAction.json index ed7178f3377..cabc4106948 100644 --- a/openmetadata-spec/src/main/resources/json/schema/alerts/alertAction.json +++ b/openmetadata-spec/src/main/resources/json/schema/alerts/alertAction.json @@ -14,7 +14,8 @@ "GenericWebhook", "SlackWebhook", "MsTeamsWebhook", - "Email" + "Email", + "ActivityFeed" ] }, "status": { @@ -56,6 +57,21 @@ }, "additionalProperties": false }, + "activityFeedAlertActionConfig": { + "description": "Activity Feed Alert Action Config", + "type": "object", + "javaType": "org.openmetadata.schema.entity.alerts.ActivityFeedAlertActionConfig", + "properties": { + "name": { + "description": "Name", + "type": "string" + }, + "description": { + "description": "Description", + "type": "string" + } + } + }, "alertActionStatus": { "description": "Alert Action Current Status", "type": "object", @@ -152,6 +168,9 @@ }, { "$ref": "./emailAlertConfig.json" + }, + { + "$ref": "#/definitions/activityFeedAlertActionConfig" } ] }, @@ -163,5 +182,7 @@ "provider" : { "$ref": "../type/basic.json#/definitions/providerType" } - } + }, + "required": ["name", "alertActionType", "alertActionConfig"], + "additionalProperties": false } diff --git a/openmetadata-spec/src/main/resources/json/schema/alerts/api/createAlertAction.json b/openmetadata-spec/src/main/resources/json/schema/alerts/api/createAlertAction.json index 27d2ae34c0c..f151faf1be1 100644 --- a/openmetadata-spec/src/main/resources/json/schema/alerts/api/createAlertAction.json +++ b/openmetadata-spec/src/main/resources/json/schema/alerts/api/createAlertAction.json @@ -51,6 +51,12 @@ "oneOf": [ { "$ref": "../../entity/events/webhook.json" + }, + { + "$ref": "../emailAlertConfig.json" + }, + { + "$ref": "../alertAction.json#/definitions/activityFeedAlertActionConfig" } ] }