diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/system/SystemResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/system/SystemResource.java index e23355491d3..02be5333807 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/system/SystemResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/system/SystemResource.java @@ -36,11 +36,13 @@ import lombok.extern.slf4j.Slf4j; import org.openmetadata.common.utils.CommonUtil; import org.openmetadata.schema.api.search.SearchSettings; import org.openmetadata.schema.auth.EmailRequest; +import org.openmetadata.schema.configuration.EntityRulesSettings; import org.openmetadata.schema.settings.Settings; import org.openmetadata.schema.settings.SettingsType; import org.openmetadata.schema.system.ValidationResponse; import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.MetadataOperation; +import org.openmetadata.schema.type.SemanticsRule; import org.openmetadata.schema.util.EntitiesCount; import org.openmetadata.schema.util.ServicesCount; import org.openmetadata.schema.utils.JsonUtils; @@ -192,6 +194,41 @@ public class SystemResource { return systemRepository.getConfigWithKey(name); } + @GET + @Path("/settings/entityRulesSettings/{entityType}") + @Operation( + operationId = "getEntityRulesSetting", + summary = "Get a setting for an entity type", + description = "Get the list of available entity rules settings for a given entity type", + responses = { + @ApiResponse( + responseCode = "200", + description = "Settings", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Settings.class))) + }) + public List getEntityRulesSettingByType( + @Context UriInfo uriInfo, + @Context SecurityContext securityContext, + @Parameter(description = "Entity Type", schema = @Schema(type = "string")) + @PathParam("entityType") + String entityType) { + return SettingsCache.getSetting(SettingsType.ENTITY_RULES_SETTINGS, EntityRulesSettings.class) + .getEntitySemantics() + .stream() + .filter(SemanticsRule::getEnabled) + .filter( + rule -> + rule.getEntityType() == null || rule.getEntityType().equalsIgnoreCase(entityType)) + .filter( + rule -> + nullOrEmpty(rule.getIgnoredEntities()) + || !rule.getIgnoredEntities().contains(entityType)) + .toList(); + } + @GET @Path("/search/nlq") @Operation( diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java index 76159286610..f3ee80ecacc 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; import static org.openmetadata.service.util.TestUtils.TEST_AUTH_HEADERS; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.dropwizard.configuration.ConfigurationException; import io.dropwizard.configuration.FileConfigurationSourceProvider; @@ -71,6 +72,7 @@ import org.openmetadata.schema.settings.Settings; import org.openmetadata.schema.settings.SettingsType; import org.openmetadata.schema.system.ValidationResponse; import org.openmetadata.schema.type.ColumnDataType; +import org.openmetadata.schema.type.SemanticsRule; import org.openmetadata.schema.util.EntitiesCount; import org.openmetadata.schema.util.ServicesCount; import org.openmetadata.schema.utils.JsonUtils; @@ -95,6 +97,7 @@ import org.openmetadata.service.resources.storages.ContainerResourceTest; import org.openmetadata.service.resources.teams.TeamResourceTest; import org.openmetadata.service.resources.teams.UserResourceTest; import org.openmetadata.service.resources.topics.TopicResourceTest; +import org.openmetadata.service.security.SecurityUtil; import org.openmetadata.service.util.TestUtils; @Slf4j @@ -1018,6 +1021,83 @@ class SystemResourceTest extends OpenMetadataApplicationTest { "The test entity type should not be added to allowedFields"); } + @Test + void testGetEntityRulesSettingByType() throws HttpResponseException { + // Test table entity type - should include only enabled rules applicable to tables + List tableRules = getEntityRules("table"); + + assertFalse(tableRules.isEmpty(), "Table rules should not be empty"); + + // Should contain general enabled rules + assertTrue( + tableRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Users or Single Team Ownership")), + "Should contain general ownership rule"); + + assertTrue( + tableRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Domains are not allowed")), + "Should contain domains rule"); + + // Should NOT contain disabled rules (even if they are table-specific) + assertFalse( + tableRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Data Products are not allowed")), + "Should not contain disabled data products rule"); + + assertFalse( + tableRules.stream() + .anyMatch(rule -> rule.getName().equals("Tables can only have a single Glossary Term")), + "Should not contain disabled table-specific glossary term rule"); + + // Test dashboard entity type - should only get general enabled rules + List dashboardRules = getEntityRules("dashboard"); + + assertFalse(dashboardRules.isEmpty(), "Dashboard rules should not be empty"); + + // Should contain general enabled rules + assertTrue( + dashboardRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Users or Single Team Ownership")), + "Dashboard should get ownership rule"); + + assertTrue( + dashboardRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Domains are not allowed")), + "Dashboard should get domains rule"); + + // Test team entity type - should get rules but exclude those that ignore team + List teamRules = getEntityRules("team"); + + assertFalse(teamRules.isEmpty(), "Team rules should not be empty"); + + // Should contain general ownership rule + assertTrue( + teamRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Users or Single Team Ownership")), + "Team should get ownership rule"); + + // Should NOT contain domains rule since team is in ignoredEntities + assertFalse( + teamRules.stream() + .anyMatch(rule -> rule.getName().equals("Multiple Domains are not allowed")), + "Team should not get domains rule as it's in ignored entities"); + } + + private static List getEntityRules(String entityType) + throws HttpResponseException { + ObjectMapper objectMapper = Jackson.newObjectMapper(); + WebTarget target = getResource("system/settings/entityRulesSettings/" + entityType); + Response response = SecurityUtil.addHeaders(target, ADMIN_AUTH_HEADERS).get(); + String responseString = response.readEntity(String.class); + try { + return objectMapper.readValue(responseString, new TypeReference>() {}); + } catch (Exception e) { + throw new HttpResponseException( + 500, "Failed to parse " + entityType + " response: " + e.getMessage()); + } + } + private static ValidationResponse getValidation() throws HttpResponseException { WebTarget target = getResource("system/status"); return TestUtils.get(target, ValidationResponse.class, ADMIN_AUTH_HEADERS);