Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
Expand Down Expand Up @@ -144,12 +145,8 @@ public static boolean shouldTriggerAlert(ChangeEvent event, FilteringRules confi
}

// Trigger Specific Settings
if (event.getEntityType().equals(THREAD)
&& (config.getResources().get(0).equals("announcement")
|| config.getResources().get(0).equals("task")
|| config.getResources().get(0).equals("conversation"))) {
Thread thread = AlertsRuleEvaluator.getThread(event);
return config.getResources().get(0).equalsIgnoreCase(thread.getType().value());
if (event.getEntityType().equals(THREAD)) {
return shouldTriggerAlertForThread(event, config.getResources().get(0));
}

// Test Suite
Expand All @@ -165,6 +162,22 @@ public static boolean shouldTriggerAlert(ChangeEvent event, FilteringRules confi
return config.getResources().contains(event.getEntityType()); // Use Trigger Specific Settings
}

private static final Set<String> THREAD_TYPE_RESOURCES =
Set.of("announcement", "task", "conversation");

private static boolean shouldTriggerAlertForThread(ChangeEvent event, String resource) {
Thread thread = AlertsRuleEvaluator.getThread(event);
if (thread == null) {
return false;
}
if (THREAD_TYPE_RESOURCES.contains(resource.toLowerCase(Locale.ROOT))) {
return resource.equalsIgnoreCase(thread.getType().value());
}
// Entity-type resource (e.g., "glossaryTerm"): match threads whose parent entity type matches
return thread.getEntityRef() != null
&& resource.equalsIgnoreCase(thread.getEntityRef().getType());
}

public static SubscriptionStatus buildSubscriptionStatus(
SubscriptionStatus.Status status,
Long lastSuccessful,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package org.openmetadata.service.events.subscription;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.openmetadata.schema.entity.events.FilteringRules;
import org.openmetadata.schema.entity.feed.Thread;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.EventType;
import org.openmetadata.schema.type.ThreadType;
import org.openmetadata.service.Entity;

class AlertUtilTest {

Expand Down Expand Up @@ -67,4 +78,189 @@ void testConvertInputListToString_consecutiveQuotes() {
String result = AlertUtil.convertInputListToString(input);
assertEquals("'test''''value'", result);
}

// ---- shouldTriggerAlert: null / "all" resource ----------------------------

@Test
void shouldTriggerAlert_nullConfig_returnsTrue() {
ChangeEvent event = entityChangeEvent("table");
assertTrue(AlertUtil.shouldTriggerAlert(event, null));
}

@Test
void shouldTriggerAlert_allResource_returnsTrue() {
ChangeEvent event = entityChangeEvent("glossaryTerm");
FilteringRules config = filteringRules("all");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

// ---- shouldTriggerAlert: entity change events ----------------------------

@Test
void shouldTriggerAlert_entityEvent_matchingResource_returnsTrue() {
ChangeEvent event = entityChangeEvent("glossaryTerm");
FilteringRules config = filteringRules("glossaryTerm");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_entityEvent_nonMatchingResource_returnsFalse() {
ChangeEvent event = entityChangeEvent("table");
FilteringRules config = filteringRules("glossaryTerm");
assertFalse(AlertUtil.shouldTriggerAlert(event, config));
}

// ---- shouldTriggerAlert: thread-type resource ("conversation" etc.) ------

@Test
void shouldTriggerAlert_conversationThread_conversationResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(glossaryTermRef());
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_CREATED);
FilteringRules config = filteringRules("conversation");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_taskThread_conversationResource_returnsFalse() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Task)
.withEntityRef(glossaryTermRef());
ChangeEvent event = threadChangeEvent(thread, EventType.TASK_CREATED);
FilteringRules config = filteringRules("conversation");
assertFalse(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_taskThread_taskResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Task)
.withEntityRef(glossaryTermRef());
ChangeEvent event = threadChangeEvent(thread, EventType.TASK_CREATED);
FilteringRules config = filteringRules("task");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_announcementThread_announcementResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Announcement)
.withEntityRef(new EntityReference().withId(UUID.randomUUID()).withType("table"));
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_CREATED);
FilteringRules config = filteringRules("announcement");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

// ---- shouldTriggerAlert: entity-type resource for thread events ----------
// These are the bug cases: thread events on a GlossaryTerm should fire
// when the subscription resource is "glossaryTerm", not just "conversation".

@Test
void shouldTriggerAlert_conversationOnGlossaryTerm_glossaryTermResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(glossaryTermRef());
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_CREATED);
FilteringRules config = filteringRules("glossaryTerm");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_threadUpdateOnGlossaryTerm_glossaryTermResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(glossaryTermRef());
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_UPDATED);
FilteringRules config = filteringRules("glossaryTerm");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_postCreatedOnGlossaryTerm_glossaryTermResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(glossaryTermRef());
ChangeEvent event = threadChangeEvent(thread, EventType.POST_CREATED);
FilteringRules config = filteringRules("glossaryTerm");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_threadOnTable_glossaryTermResource_returnsFalse() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(new EntityReference().withId(UUID.randomUUID()).withType("table"));
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_CREATED);
FilteringRules config = filteringRules("glossaryTerm");
assertFalse(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_threadOnTable_tableResource_returnsTrue() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(new EntityReference().withId(UUID.randomUUID()).withType("table"));
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_CREATED);
FilteringRules config = filteringRules("table");
assertTrue(AlertUtil.shouldTriggerAlert(event, config));
}

@Test
void shouldTriggerAlert_threadWithNullEntityRef_entityTypeResource_returnsFalse() {
Thread thread =
new Thread()
.withId(UUID.randomUUID())
.withType(ThreadType.Conversation)
.withEntityRef(null);
ChangeEvent event = threadChangeEvent(thread, EventType.THREAD_CREATED);
FilteringRules config = filteringRules("glossaryTerm");
assertFalse(AlertUtil.shouldTriggerAlert(event, config));
}

// ---- helpers ---------------------------------------------------------------

private static ChangeEvent entityChangeEvent(String entityType) {
return new ChangeEvent()
.withId(UUID.randomUUID())
.withEventType(EventType.ENTITY_UPDATED)
.withEntityType(entityType);
}

private static ChangeEvent threadChangeEvent(Thread thread, EventType eventType) {
return new ChangeEvent()
.withId(UUID.randomUUID())
.withEventType(eventType)
.withEntityType(Entity.THREAD)
.withEntity(thread);
}

private static FilteringRules filteringRules(String resource) {
return new FilteringRules()
.withResources(List.of(resource))
.withRules(Collections.emptyList())
.withActions(Collections.emptyList());
}

private static EntityReference glossaryTermRef() {
return new EntityReference().withId(UUID.randomUUID()).withType("glossaryTerm");
}
}
Loading