Skip to content

Commit bedcb37

Browse files
ATLAS-5005 : Basic search entity filter validation (#333)
1 parent 4b702b0 commit bedcb37

File tree

6 files changed

+256
-0
lines changed

6 files changed

+256
-0
lines changed

intg/src/main/java/org/apache/atlas/AtlasErrorCode.java

+3
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ public enum AtlasErrorCode {
179179
LINEAGE_ON_DEMAND_NOT_ENABLED(400, "ATLAS-400-00-100", "Lineage on demand config: {0} is not enabled"),
180180
INVALID_TOPIC_NAME(400, "ATLAS-400-00-101", "Unsupported topic name : {0}"),
181181
UNSUPPORTED_TYPE_NAME(400, "ATLAS-400-00-102", "Unsupported {0} name. Names such as 'description','version','options','name','servicetype' are not supported"),
182+
INVALID_OPERATOR(400, "ATLAS-400-00-103", "Invalid operator specified for attribute: {0}"),
183+
BLANK_NAME_ATTRIBUTE(400, "ATLAS-400-00-104", "Name Attribute can't be empty!"),
184+
BLANK_VALUE_ATTRIBUTE(400, "ATLAS-400-00-105", "Value Attribute can't be empty!"),
182185

183186
UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to perform {1}"),
184187

webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java

+43
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,50 @@ private void validateSearchParameters(SearchParameters parameters) throws AtlasB
885885
if (StringUtils.isNotEmpty(parameters.getQuery()) && parameters.getQuery().length() > maxFullTextQueryLength) {
886886
throw new AtlasBaseException(AtlasErrorCode.INVALID_QUERY_LENGTH, Constants.MAX_FULLTEXT_QUERY_STR_LENGTH);
887887
}
888+
889+
validateEntityFilter(parameters);
890+
}
891+
}
892+
893+
private void validateEntityFilter(SearchParameters parameters) throws AtlasBaseException {
894+
FilterCriteria entityFilter = parameters.getEntityFilters();
895+
896+
if (entityFilter == null) {
897+
return;
898+
}
899+
900+
if (entityFilter.getCriterion() != null &&
901+
!entityFilter.getCriterion().isEmpty()) {
902+
if (entityFilter.getCondition() == null || StringUtils.isEmpty(entityFilter.getCondition().toString())) {
903+
throw new AtlasBaseException("Condition (AND/OR) must be specified when using multiple filters.");
904+
}
905+
906+
for (FilterCriteria filterCriteria : entityFilter.getCriterion()) {
907+
validateCriteria(filterCriteria);
908+
}
888909
}
910+
else {
911+
validateCriteria(entityFilter);
912+
}
913+
}
914+
915+
private void validateCriteria(SearchParameters.FilterCriteria criteria) throws AtlasBaseException {
916+
if (criteria.getOperator() == null) {
917+
throw new AtlasBaseException(AtlasErrorCode.INVALID_OPERATOR, criteria.getAttributeName());
918+
}
919+
920+
if (StringUtils.isBlank(criteria.getAttributeName())) {
921+
throw new AtlasBaseException(AtlasErrorCode.BLANK_NAME_ATTRIBUTE);
922+
}
923+
924+
if (requiresValue(criteria.getOperator()) && StringUtils.isBlank(criteria.getAttributeValue())) {
925+
throw new AtlasBaseException(AtlasErrorCode.BLANK_VALUE_ATTRIBUTE);
926+
}
927+
}
928+
929+
private boolean requiresValue(SearchParameters.Operator operator) {
930+
return operator != SearchParameters.Operator.IS_NULL
931+
&& operator != SearchParameters.Operator.NOT_NULL;
889932
}
890933

891934
private void validateSearchParameters(QuickSearchParameters parameters) throws AtlasBaseException {

webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java

+40
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,18 @@
4242
import java.io.IOException;
4343
import java.net.URLEncoder;
4444
import java.util.ArrayList;
45+
import java.util.Arrays;
4546
import java.util.Collections;
4647
import java.util.HashMap;
4748
import java.util.List;
49+
import java.util.function.Predicate;
4850

4951
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
5052
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.PUBLIC_ONLY;
5153
import static org.testng.Assert.assertEquals;
5254
import static org.testng.Assert.assertNotNull;
5355
import static org.testng.Assert.assertNull;
56+
import static org.testng.Assert.assertTrue;
5457
import static org.testng.Assert.fail;
5558

5659
public class BasicSearchIT extends BaseResourceIT {
@@ -224,6 +227,43 @@ public void testSavedSearch(String jsonFile) {
224227
}
225228
}
226229

230+
@Test
231+
public void testAttributeSearchInvalidOperator() {
232+
runNegativeSearchTest("search-parameters/operator", "ATLAS-400-00-103", parameters -> parameters.getEntityFilters() != null && parameters.getEntityFilters().getOperator() != null);
233+
}
234+
235+
@Test
236+
public void testAttributeSearchEmptyNameAttribute() {
237+
runNegativeSearchTest("search-parameters/attribute-name", "ATLAS-400-00-104", parameters -> parameters.getEntityFilters() != null && parameters.getEntityFilters().getAttributeName() != null);
238+
}
239+
240+
@Test
241+
public void testAttributeSearchEmptyValueAttribute() {
242+
runNegativeSearchTest("search-parameters/attribute-value", "ATLAS-400-00-105", parameters -> parameters.getEntityFilters() != null && parameters.getEntityFilters().getAttributeValue() != null);
243+
}
244+
245+
public void runNegativeSearchTest(String jsonFile, String expectedErrorCode, java.util.function.Predicate<SearchParameters> paramFilter) {
246+
try {
247+
BasicSearchParametersWithExpectation[] testExpectations = TestResourceFileUtils.readObjectFromJson(jsonFile, BasicSearchParametersWithExpectation[].class);
248+
assertNotNull(testExpectations);
249+
Arrays
250+
.stream(testExpectations)
251+
.map(testExpectation -> testExpectation.getSearchParameters())
252+
.filter(paramFilter)
253+
.forEach(params -> {
254+
try {
255+
atlasClientV2.facetedSearch(params);
256+
}
257+
catch (AtlasServiceException e) {
258+
assertTrue(e.getMessage().contains(expectedErrorCode),
259+
"Expected error code " + expectedErrorCode + " in exception message: " + e.getMessage());
260+
}
261+
});
262+
} catch (IOException e) {
263+
fail(e.getMessage());
264+
}
265+
}
266+
227267
@Test(dependsOnMethods = "testSavedSearch")
228268
public void testExecuteSavedSearchByName() {
229269
try {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[ {
2+
"testDescription": "hive_table contains testtable or retentionSize != 0",
3+
"searchParameters": {
4+
"typeName": "hive_table",
5+
"excludeDeletedEntities": true,
6+
"classification": "",
7+
"query": "",
8+
"limit": 25,
9+
"offset": 0,
10+
"entityFilters": {
11+
"attributeName": "",
12+
"attributeValue": "",
13+
"condition" : "AND",
14+
"criterion" : [
15+
{
16+
"attributeName": "",
17+
"operator": "eq",
18+
"attributeValue": "testtable"
19+
},
20+
{
21+
"attributeName": "retention",
22+
"operator": "neq",
23+
"attributeValue": "0"
24+
}
25+
]
26+
},
27+
"tagFilters": null,
28+
"attributes": [
29+
""
30+
]
31+
},
32+
"expectedCount": 0
33+
}
34+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[ {
2+
"testDescription": "hive_table contains testtable or retentionSize != 0",
3+
"searchParameters": {
4+
"typeName": "hive_table",
5+
"excludeDeletedEntities": true,
6+
"classification": "",
7+
"query": "",
8+
"limit": 25,
9+
"offset": 0,
10+
"entityFilters": {
11+
"attributeName": "name",
12+
"attributeValue": "",
13+
"condition" : "AND",
14+
"criterion" : [
15+
{
16+
"attributeName": "name",
17+
"operator": "eq",
18+
"attributeValue": ""
19+
},
20+
{
21+
"attributeName": "retention",
22+
"operator": "neq",
23+
"attributeValue": ""
24+
}
25+
]
26+
},
27+
"tagFilters": null,
28+
"attributes": [
29+
""
30+
]
31+
},
32+
"expectedCount": 0
33+
}
34+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[ {
2+
"testDescription": "hive_table contains testtable or retentionSize != 0",
3+
"searchParameters": {
4+
"typeName": "hive_table",
5+
"excludeDeletedEntities": true,
6+
"classification": "",
7+
"query": "",
8+
"limit": 25,
9+
"offset": 0,
10+
"entityFilters": {
11+
"attributeName": "name",
12+
"attributeValue": "testtable",
13+
"condition" : "AND",
14+
"criterion" : [
15+
{
16+
"attributeName": "name",
17+
"operator": "invalid_contains",
18+
"attributeValue": "testtable"
19+
},
20+
{
21+
"attributeName": "retention",
22+
"operator": "neq",
23+
"attributeValue": "0"
24+
}
25+
]
26+
},
27+
"tagFilters": null,
28+
"attributes": [
29+
""
30+
]
31+
},
32+
"expectedCount": 0
33+
},
34+
35+
{
36+
"testDescription": "hive_table contains testtable or retentionSize != 0",
37+
"searchParameters": {
38+
"typeName": "hive_table",
39+
"excludeDeletedEntities": true,
40+
"classification": "",
41+
"query": "",
42+
"limit": 25,
43+
"offset": 0,
44+
"entityFilters": {
45+
"attributeName": "name",
46+
"attributeValue": "testtable",
47+
"condition" : "OR",
48+
"criterion" : [
49+
{
50+
"attributeName": "name",
51+
"operator": "invalid_eq",
52+
"attributeValue": "testtable"
53+
},
54+
{
55+
"attributeName": "retention",
56+
"operator": "invalid_eq",
57+
"attributeValue": "0"
58+
}
59+
]
60+
},
61+
"tagFilters": null,
62+
"attributes": [
63+
""
64+
]
65+
},
66+
"expectedCount": 0
67+
},
68+
69+
{
70+
"testDescription": "hive_table contains testtable or retentionSize != 0",
71+
"searchParameters": {
72+
"typeName": "hive_table",
73+
"excludeDeletedEntities": true,
74+
"classification": "",
75+
"query": "",
76+
"limit": 25,
77+
"offset": 0,
78+
"entityFilters": {
79+
"attributeName": "name",
80+
"attributeValue": "testtable",
81+
"condition" : "OR",
82+
"criterion" : [
83+
{
84+
"attributeName": "name",
85+
"operator": "invalid_neq",
86+
"attributeValue": "testtable"
87+
},
88+
{
89+
"attributeName": "retention",
90+
"operator": "invalid_contains",
91+
"attributeValue": "0"
92+
}
93+
]
94+
},
95+
"tagFilters": null,
96+
"attributes": [
97+
""
98+
]
99+
},
100+
"expectedCount": 0
101+
}
102+
]

0 commit comments

Comments
 (0)