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
+  }
+]

Reply via email to