[GEN-876] Implement DQ dimension filter for test cases (#17032)

* fix: typo in key for data dimension

* fix: table test key typo in data dimension

* fix: typo in migration util

* fix: add filter test cases by dimensions in search API

* style: ran java linting
This commit is contained in:
Teddy 2024-07-16 08:12:39 +02:00 committed by GitHub
parent d10c129ed3
commit 924a100ab4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 66 additions and 37 deletions

View File

@ -1068,8 +1068,13 @@ public abstract class EntityRepository<T extends EntityInterface> {
entityList.add(withHref(uriInfo, entity)); entityList.add(withHref(uriInfo, entity));
} }
return new ResultList<>(entityList, offset, limit, total.intValue()); return new ResultList<>(entityList, offset, limit, total.intValue());
} else {
SearchClient.SearchResultListMapper results =
searchRepository.listWithOffset(
searchListFilter, limit, offset, entityType, searchSortFilter, q);
total = results.getTotal();
return new ResultList<>(entityList, null, limit, total.intValue());
} }
throw new IllegalArgumentException("Limit should be greater than 0");
} }
@Transaction @Transaction

View File

@ -64,7 +64,7 @@ public class MigrationUtil {
testCaseDefinition.getFullyQualifiedName()); testCaseDefinition.getFullyQualifiedName());
return; return;
} }
testCaseDefinition.setDatatQualityDimension(dimension); testCaseDefinition.setDataQualityDimension(dimension);
collectionDAO.testDefinitionDAO().update(testCaseDefinition); collectionDAO.testDefinitionDAO().update(testCaseDefinition);
} catch (Exception e) { } catch (Exception e) {
LOG.warn("Error migrating test case dimension", e); LOG.warn("Error migrating test case dimension", e);

View File

@ -91,7 +91,7 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
static final String FIELDS = "owner,testSuite,testDefinition,testSuites,incidentId,domain,tags"; static final String FIELDS = "owner,testSuite,testDefinition,testSuites,incidentId,domain,tags";
static final String SEARCH_FIELDS_EXCLUDE = static final String SEARCH_FIELDS_EXCLUDE =
"testPlatforms,table,database,databaseSchema,service,testSuite"; "testPlatforms,table,database,databaseSchema,service,testSuite,dataQualityDimension";
@Override @Override
public TestCase addHref(UriInfo uriInfo, TestCase test) { public TestCase addHref(UriInfo uriInfo, TestCase test) {
@ -316,6 +316,12 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
schema = @Schema(type = "string")) schema = @Schema(type = "string"))
@QueryParam("testPlatforms") @QueryParam("testPlatforms")
String testPlatforms, String testPlatforms,
@Parameter(
description =
"Filter for test case by data quality dimension (e.g. OpenMetadata, dbt, etc.)",
schema = @Schema(type = "string"))
@QueryParam("dataQualityDimension")
String dataQualityDimension,
@Parameter( @Parameter(
description = description =
"Parameter used to filter (inclusive) the test cases by the last execution timestamp (in milliseconds). Must be used in conjunction with `endTimestamp`", "Parameter used to filter (inclusive) the test cases by the last execution timestamp (in milliseconds). Must be used in conjunction with `endTimestamp`",
@ -392,6 +398,7 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
searchListFilter.addQueryParam("testCaseStatus", status); searchListFilter.addQueryParam("testCaseStatus", status);
searchListFilter.addQueryParam("testCaseType", type); searchListFilter.addQueryParam("testCaseType", type);
searchListFilter.addQueryParam("testPlatforms", testPlatforms); searchListFilter.addQueryParam("testPlatforms", testPlatforms);
searchListFilter.addQueryParam("dataQualityDimension", dataQualityDimension);
searchListFilter.addQueryParam("q", q); searchListFilter.addQueryParam("q", q);
searchListFilter.addQueryParam("excludeFields", SEARCH_FIELDS_EXCLUDE); searchListFilter.addQueryParam("excludeFields", SEARCH_FIELDS_EXCLUDE);
searchListFilter.addQueryParam("includeFields", includeFields); searchListFilter.addQueryParam("includeFields", includeFields);

View File

@ -141,6 +141,7 @@ public class SearchListFilter extends Filter<SearchListFilter> {
String tags = getQueryParam("tags"); String tags = getQueryParam("tags");
String tier = getQueryParam("tier"); String tier = getQueryParam("tier");
String serviceName = getQueryParam("serviceName"); String serviceName = getQueryParam("serviceName");
String dataQualityDimension = getQueryParam("dataQualityDimension");
if (tags != null) { if (tags != null) {
String tagsList = String tagsList =
@ -209,6 +210,11 @@ public class SearchListFilter extends Filter<SearchListFilter> {
getTimestampFilter("testCaseResult.timestamp", "lte", Long.parseLong(endTimestamp))); getTimestampFilter("testCaseResult.timestamp", "lte", Long.parseLong(endTimestamp)));
} }
if (dataQualityDimension != null) {
conditions.add(
String.format("{\"term\": {\"dataQualityDimension\": \"%s\"}}", dataQualityDimension));
}
return addCondition(conditions); return addCondition(conditions);
} }

View File

@ -4,11 +4,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.openmetadata.schema.tests.TestCase; import org.openmetadata.schema.tests.TestCase;
import org.openmetadata.schema.tests.TestDefinition; import org.openmetadata.schema.tests.TestDefinition;
import org.openmetadata.schema.tests.TestPlatform;
import org.openmetadata.schema.tests.TestSuite; import org.openmetadata.schema.tests.TestSuite;
import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.Include;
@ -33,6 +31,9 @@ public record TestCaseIndex(TestCase testCase) implements SearchIndex {
public Map<String, Object> buildSearchIndexDocInternal(Map<String, Object> doc) { public Map<String, Object> buildSearchIndexDocInternal(Map<String, Object> doc) {
// Build Index Doc // Build Index Doc
List<SearchSuggest> suggest = new ArrayList<>(); List<SearchSuggest> suggest = new ArrayList<>();
TestDefinition testDefinition =
Entity.getEntity(
Entity.TEST_DEFINITION, testCase.getTestDefinition().getId(), "", Include.ALL);
suggest.add(SearchSuggest.builder().input(testCase.getFullyQualifiedName()).weight(5).build()); suggest.add(SearchSuggest.builder().input(testCase.getFullyQualifiedName()).weight(5).build());
suggest.add(SearchSuggest.builder().input(testCase.getName()).weight(10).build()); suggest.add(SearchSuggest.builder().input(testCase.getName()).weight(10).build());
doc.put( doc.put(
@ -44,18 +45,13 @@ public record TestCaseIndex(TestCase testCase) implements SearchIndex {
doc.put("entityType", Entity.TEST_CASE); doc.put("entityType", Entity.TEST_CASE);
doc.put("owner", getEntityWithDisplayName(testCase.getOwner())); doc.put("owner", getEntityWithDisplayName(testCase.getOwner()));
doc.put("tags", testCase.getTags()); doc.put("tags", testCase.getTags());
doc.put("testPlatforms", getTestDefinitionPlatforms(testCase.getTestDefinition().getId())); doc.put("testPlatforms", testDefinition.getTestPlatforms());
doc.put("dataQualityDimension", testDefinition.getDataQualityDimension());
doc.put("followers", SearchIndexUtils.parseFollowers(testCase.getFollowers())); doc.put("followers", SearchIndexUtils.parseFollowers(testCase.getFollowers()));
setParentRelationships(doc, testCase); setParentRelationships(doc, testCase);
return doc; return doc;
} }
private List<TestPlatform> getTestDefinitionPlatforms(UUID testDefinitionId) {
TestDefinition testDefinition =
Entity.getEntity(Entity.TEST_DEFINITION, testDefinitionId, "", Include.ALL);
return testDefinition.getTestPlatforms();
}
private void setParentRelationships(Map<String, Object> doc, TestCase testCase) { private void setParentRelationships(Map<String, Object> doc, TestCase testCase) {
// denormalize the parent relationships for search // denormalize the parent relationships for search
EntityReference testSuiteEntityReference = testCase.getTestSuite(); EntityReference testSuiteEntityReference = testCase.getTestSuite();

View File

@ -169,6 +169,9 @@
"entityType": { "entityType": {
"type": "keyword" "type": "keyword"
}, },
"dataQualityDimension": {
"type": "keyword"
},
"suggest": { "suggest": {
"type": "completion", "type": "completion",
"contexts": [ "contexts": [

View File

@ -192,6 +192,9 @@
"entityType": { "entityType": {
"type": "keyword" "type": "keyword"
}, },
"dataQualityDimension": {
"type": "keyword"
},
"suggest": { "suggest": {
"type": "completion", "type": "completion",
"contexts": [ "contexts": [

View File

@ -215,6 +215,9 @@
"entityType": { "entityType": {
"type": "keyword" "type": "keyword"
}, },
"dataQualityDimension": {
"type": "keyword"
},
"suggest": { "suggest": {
"type": "completion", "type": "completion",
"contexts": [ "contexts": [

View File

@ -30,6 +30,6 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -30,6 +30,6 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -30,6 +30,6 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -30,6 +30,6 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -30,6 +30,6 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -31,5 +31,5 @@
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -22,5 +22,5 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Completeness" "dataQualityDimension": "Completeness"
} }

View File

@ -30,6 +30,6 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -31,5 +31,5 @@
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Accuracy" "dataQualityDimension": "Accuracy"
} }

View File

@ -24,5 +24,5 @@
], ],
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Validity" "dataQualityDimension": "Validity"
} }

View File

@ -17,5 +17,5 @@
], ],
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Validity" "dataQualityDimension": "Validity"
} }

View File

@ -8,5 +8,5 @@
"supportedDataTypes": ["NUMBER","TINYINT","SMALLINT","INT","BIGINT","BYTEINT","BYTES","FLOAT","DOUBLE","DECIMAL","NUMERIC","TIMESTAMP","TIMESTAMPZ","TIME","DATE","DATETIME","INTERVAL","STRING","MEDIUMTEXT","TEXT","CHAR","VARCHAR","BOOLEAN","BINARY","VARBINARY","ARRAY","BLOB","LONGBLOB","MEDIUMBLOB","MAP","STRUCT","UNION","SET","GEOGRAPHY","ENUM","JSON","UUID","VARIANT","GEOMETRY","POINT","POLYGON"], "supportedDataTypes": ["NUMBER","TINYINT","SMALLINT","INT","BIGINT","BYTEINT","BYTES","FLOAT","DOUBLE","DECIMAL","NUMERIC","TIMESTAMP","TIMESTAMPZ","TIME","DATE","DATETIME","INTERVAL","STRING","MEDIUMTEXT","TEXT","CHAR","VARCHAR","BOOLEAN","BINARY","VARBINARY","ARRAY","BLOB","LONGBLOB","MEDIUMBLOB","MAP","STRUCT","UNION","SET","GEOGRAPHY","ENUM","JSON","UUID","VARIANT","GEOMETRY","POINT","POLYGON"],
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Completeness" "dataQualityDimension": "Completeness"
} }

View File

@ -8,5 +8,5 @@
"supportedDataTypes": ["NUMBER","TINYINT","SMALLINT","INT","BIGINT","BYTEINT","BYTES","FLOAT","DOUBLE","DECIMAL","NUMERIC","TIMESTAMP","TIMESTAMPZ","TIME","DATE","DATETIME","INTERVAL","STRING","MEDIUMTEXT","TEXT","CHAR","VARCHAR","BOOLEAN","BINARY","VARBINARY","ARRAY","BLOB","LONGBLOB","MEDIUMBLOB","MAP","STRUCT","UNION","SET","GEOGRAPHY","ENUM","JSON","UUID","VARIANT","GEOMETRY","POINT","POLYGON"], "supportedDataTypes": ["NUMBER","TINYINT","SMALLINT","INT","BIGINT","BYTEINT","BYTES","FLOAT","DOUBLE","DECIMAL","NUMERIC","TIMESTAMP","TIMESTAMPZ","TIME","DATE","DATETIME","INTERVAL","STRING","MEDIUMTEXT","TEXT","CHAR","VARCHAR","BOOLEAN","BINARY","VARBINARY","ARRAY","BLOB","LONGBLOB","MEDIUMBLOB","MAP","STRUCT","UNION","SET","GEOGRAPHY","ENUM","JSON","UUID","VARIANT","GEOMETRY","POINT","POLYGON"],
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Uniqueness" "dataQualityDimension": "Uniqueness"
} }

View File

@ -17,5 +17,5 @@
], ],
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Validity" "dataQualityDimension": "Validity"
} }

View File

@ -17,5 +17,5 @@
], ],
"supportsRowLevelPassedFailed": true, "supportsRowLevelPassedFailed": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Validity" "dataQualityDimension": "Validity"
} }

View File

@ -28,5 +28,5 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -15,5 +15,5 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -15,6 +15,6 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -21,6 +21,6 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -30,6 +30,6 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "SQL" "dataQualityDimension": "SQL"
} }

View File

@ -45,6 +45,6 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Consistency" "dataQualityDimension": "Consistency"
} }

View File

@ -29,5 +29,5 @@
], ],
"supportsDynamicAssertion": true, "supportsDynamicAssertion": true,
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -15,5 +15,5 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -51,6 +51,6 @@
} }
], ],
"provider": "system", "provider": "system",
"datatQualityDimension": "Integrity" "dataQualityDimension": "Integrity"
} }

View File

@ -924,6 +924,12 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
assertNotNull(testCase.getEntityLink()); assertNotNull(testCase.getEntityLink());
assertNotNull(testCase.getName()); assertNotNull(testCase.getName());
assertNotNull(testCase.getId()); assertNotNull(testCase.getId());
// Test return only the specified dimension
queryParams.clear();
queryParams.put("dataQualityDimension", "Completeness");
allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
assertNotEquals(0, allEntities.getData().size());
} }
@Test @Test

View File

@ -162,7 +162,7 @@
"$ref": "#/definitions/testCaseParameterDefinition" "$ref": "#/definitions/testCaseParameterDefinition"
} }
}, },
"datatQualityDimension": { "dataQualityDimension": {
"$ref": "#/definitions/dataQualityDimensions" "$ref": "#/definitions/dataQualityDimensions"
}, },
"owner": { "owner": {