This is an automated email from the ASF dual-hosted git repository.
pinal pushed a commit to branch atlas-2.5
in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/atlas-2.5 by this push:
new 9c47ad573 ATLAS-5005 : Basic search entity filter validation (#333)
9c47ad573 is described below
commit 9c47ad573e6725649f6c1a2e4cbe5fc9530c1211
Author: Aditya Gupta <[email protected]>
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 5dfaa98cb..9256d0f88 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
+ }
+]