This is an automated email from the ASF dual-hosted git repository. chaitalithombare pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/master by this push: new bedcb37a5 ATLAS-5005 : Basic search entity filter validation (#333) bedcb37a5 is described below commit bedcb37a5bc24a724ef0342c93a8ab5257282445 Author: Aditya Gupta <aditya.gu...@freestoneinfotech.com> AuthorDate: Tue Apr 22 14:54:32 2025 +0530 ATLAS-5005 : Basic search entity filter validation (#333) --- .../main/java/org/apache/atlas/AtlasErrorCode.java | 3 + .../org/apache/atlas/web/rest/DiscoveryREST.java | 43 +++++++++ .../atlas/web/integration/BasicSearchIT.java | 40 ++++++++ .../json/search-parameters/attribute-name.json | 34 +++++++ .../json/search-parameters/attribute-value.json | 34 +++++++ .../resources/json/search-parameters/operator.json | 102 +++++++++++++++++++++ 6 files changed, 256 insertions(+) diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java index 97d83b6fe..521d19c01 100644 --- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java +++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java @@ -179,6 +179,9 @@ public enum AtlasErrorCode { LINEAGE_ON_DEMAND_NOT_ENABLED(400, "ATLAS-400-00-100", "Lineage on demand config: {0} is not enabled"), INVALID_TOPIC_NAME(400, "ATLAS-400-00-101", "Unsupported topic name : {0}"), UNSUPPORTED_TYPE_NAME(400, "ATLAS-400-00-102", "Unsupported {0} name. Names such as 'description','version','options','name','servicetype' are not supported"), + INVALID_OPERATOR(400, "ATLAS-400-00-103", "Invalid operator specified for attribute: {0}"), + BLANK_NAME_ATTRIBUTE(400, "ATLAS-400-00-104", "Name Attribute can't be empty!"), + BLANK_VALUE_ATTRIBUTE(400, "ATLAS-400-00-105", "Value Attribute can't be empty!"), UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to perform {1}"), diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java index 2653e5c11..773b38765 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java @@ -885,7 +885,50 @@ public class DiscoveryREST { if (StringUtils.isNotEmpty(parameters.getQuery()) && parameters.getQuery().length() > maxFullTextQueryLength) { throw new AtlasBaseException(AtlasErrorCode.INVALID_QUERY_LENGTH, Constants.MAX_FULLTEXT_QUERY_STR_LENGTH); } + + validateEntityFilter(parameters); + } + } + + private void validateEntityFilter(SearchParameters parameters) throws AtlasBaseException { + FilterCriteria entityFilter = parameters.getEntityFilters(); + + if (entityFilter == null) { + return; + } + + if (entityFilter.getCriterion() != null && + !entityFilter.getCriterion().isEmpty()) { + if (entityFilter.getCondition() == null || StringUtils.isEmpty(entityFilter.getCondition().toString())) { + throw new AtlasBaseException("Condition (AND/OR) must be specified when using multiple filters."); + } + + for (FilterCriteria filterCriteria : entityFilter.getCriterion()) { + validateCriteria(filterCriteria); + } } + else { + validateCriteria(entityFilter); + } + } + + private void validateCriteria(SearchParameters.FilterCriteria criteria) throws AtlasBaseException { + if (criteria.getOperator() == null) { + throw new AtlasBaseException(AtlasErrorCode.INVALID_OPERATOR, criteria.getAttributeName()); + } + + if (StringUtils.isBlank(criteria.getAttributeName())) { + throw new AtlasBaseException(AtlasErrorCode.BLANK_NAME_ATTRIBUTE); + } + + if (requiresValue(criteria.getOperator()) && StringUtils.isBlank(criteria.getAttributeValue())) { + throw new AtlasBaseException(AtlasErrorCode.BLANK_VALUE_ATTRIBUTE); + } + } + + private boolean requiresValue(SearchParameters.Operator operator) { + return operator != SearchParameters.Operator.IS_NULL + && operator != SearchParameters.Operator.NOT_NULL; } private void validateSearchParameters(QuickSearchParameters parameters) throws AtlasBaseException { diff --git a/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java b/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java index f5ed734fa..b851608f9 100644 --- a/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java +++ b/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java @@ -42,15 +42,18 @@ import org.testng.annotations.Test; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.function.Predicate; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.PUBLIC_ONLY; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; public class BasicSearchIT extends BaseResourceIT { @@ -224,6 +227,43 @@ public class BasicSearchIT extends BaseResourceIT { } } + @Test + public void testAttributeSearchInvalidOperator() { + runNegativeSearchTest("search-parameters/operator", "ATLAS-400-00-103", parameters -> parameters.getEntityFilters() != null && parameters.getEntityFilters().getOperator() != null); + } + + @Test + public void testAttributeSearchEmptyNameAttribute() { + runNegativeSearchTest("search-parameters/attribute-name", "ATLAS-400-00-104", parameters -> parameters.getEntityFilters() != null && parameters.getEntityFilters().getAttributeName() != null); + } + + @Test + public void testAttributeSearchEmptyValueAttribute() { + runNegativeSearchTest("search-parameters/attribute-value", "ATLAS-400-00-105", parameters -> parameters.getEntityFilters() != null && parameters.getEntityFilters().getAttributeValue() != null); + } + + public void runNegativeSearchTest(String jsonFile, String expectedErrorCode, java.util.function.Predicate<SearchParameters> paramFilter) { + try { + BasicSearchParametersWithExpectation[] testExpectations = TestResourceFileUtils.readObjectFromJson(jsonFile, BasicSearchParametersWithExpectation[].class); + assertNotNull(testExpectations); + Arrays + .stream(testExpectations) + .map(testExpectation -> testExpectation.getSearchParameters()) + .filter(paramFilter) + .forEach(params -> { + try { + atlasClientV2.facetedSearch(params); + } + catch (AtlasServiceException e) { + assertTrue(e.getMessage().contains(expectedErrorCode), + "Expected error code " + expectedErrorCode + " in exception message: " + e.getMessage()); + } + }); + } catch (IOException e) { + fail(e.getMessage()); + } + } + @Test(dependsOnMethods = "testSavedSearch") public void testExecuteSavedSearchByName() { try { diff --git a/webapp/src/test/resources/json/search-parameters/attribute-name.json b/webapp/src/test/resources/json/search-parameters/attribute-name.json new file mode 100644 index 000000000..084b674f2 --- /dev/null +++ b/webapp/src/test/resources/json/search-parameters/attribute-name.json @@ -0,0 +1,34 @@ +[ { + "testDescription": "hive_table contains testtable or retentionSize != 0", + "searchParameters": { + "typeName": "hive_table", + "excludeDeletedEntities": true, + "classification": "", + "query": "", + "limit": 25, + "offset": 0, + "entityFilters": { + "attributeName": "", + "attributeValue": "", + "condition" : "AND", + "criterion" : [ + { + "attributeName": "", + "operator": "eq", + "attributeValue": "testtable" + }, + { + "attributeName": "retention", + "operator": "neq", + "attributeValue": "0" + } + ] + }, + "tagFilters": null, + "attributes": [ + "" + ] + }, + "expectedCount": 0 +} +] diff --git a/webapp/src/test/resources/json/search-parameters/attribute-value.json b/webapp/src/test/resources/json/search-parameters/attribute-value.json new file mode 100644 index 000000000..5bd367667 --- /dev/null +++ b/webapp/src/test/resources/json/search-parameters/attribute-value.json @@ -0,0 +1,34 @@ +[ { + "testDescription": "hive_table contains testtable or retentionSize != 0", + "searchParameters": { + "typeName": "hive_table", + "excludeDeletedEntities": true, + "classification": "", + "query": "", + "limit": 25, + "offset": 0, + "entityFilters": { + "attributeName": "name", + "attributeValue": "", + "condition" : "AND", + "criterion" : [ + { + "attributeName": "name", + "operator": "eq", + "attributeValue": "" + }, + { + "attributeName": "retention", + "operator": "neq", + "attributeValue": "" + } + ] + }, + "tagFilters": null, + "attributes": [ + "" + ] + }, + "expectedCount": 0 +} +] diff --git a/webapp/src/test/resources/json/search-parameters/operator.json b/webapp/src/test/resources/json/search-parameters/operator.json new file mode 100644 index 000000000..224657448 --- /dev/null +++ b/webapp/src/test/resources/json/search-parameters/operator.json @@ -0,0 +1,102 @@ +[ { + "testDescription": "hive_table contains testtable or retentionSize != 0", + "searchParameters": { + "typeName": "hive_table", + "excludeDeletedEntities": true, + "classification": "", + "query": "", + "limit": 25, + "offset": 0, + "entityFilters": { + "attributeName": "name", + "attributeValue": "testtable", + "condition" : "AND", + "criterion" : [ + { + "attributeName": "name", + "operator": "invalid_contains", + "attributeValue": "testtable" + }, + { + "attributeName": "retention", + "operator": "neq", + "attributeValue": "0" + } + ] + }, + "tagFilters": null, + "attributes": [ + "" + ] + }, + "expectedCount": 0 +}, + + { + "testDescription": "hive_table contains testtable or retentionSize != 0", + "searchParameters": { + "typeName": "hive_table", + "excludeDeletedEntities": true, + "classification": "", + "query": "", + "limit": 25, + "offset": 0, + "entityFilters": { + "attributeName": "name", + "attributeValue": "testtable", + "condition" : "OR", + "criterion" : [ + { + "attributeName": "name", + "operator": "invalid_eq", + "attributeValue": "testtable" + }, + { + "attributeName": "retention", + "operator": "invalid_eq", + "attributeValue": "0" + } + ] + }, + "tagFilters": null, + "attributes": [ + "" + ] + }, + "expectedCount": 0 + }, + + { + "testDescription": "hive_table contains testtable or retentionSize != 0", + "searchParameters": { + "typeName": "hive_table", + "excludeDeletedEntities": true, + "classification": "", + "query": "", + "limit": 25, + "offset": 0, + "entityFilters": { + "attributeName": "name", + "attributeValue": "testtable", + "condition" : "OR", + "criterion" : [ + { + "attributeName": "name", + "operator": "invalid_neq", + "attributeValue": "testtable" + }, + { + "attributeName": "retention", + "operator": "invalid_contains", + "attributeValue": "0" + } + ] + }, + "tagFilters": null, + "attributes": [ + "" + ] + }, + "expectedCount": 0 + } +]