From 4ed87a4d08105ab325c5933b31bbedcd18b365db Mon Sep 17 00:00:00 2001 From: Teddy Date: Thu, 25 Apr 2024 17:31:11 +0200 Subject: [PATCH] Fix #15341 - Test Case reference as inherited field for Test Case Incident (#16027) * fix: unique test computation to scalar_subquery * fix: make test case reference an inherited field * style: ran java linting * fix: added test case resolution migration * style: ran java linting --- .../sqlalchemy/columnValuesToBeUnique.py | 2 +- .../mysql_test_container.py | 1 + .../postgres_test_container.py | 1 + .../service/jdbi3/EntityRepository.java | 10 +- .../service/jdbi3/EntityTimeSeriesDAO.java | 7 ++ .../jdbi3/EntityTimeSeriesRepository.java | 116 ++++++++++++++++-- .../service/jdbi3/FeedRepository.java | 14 +++ .../service/jdbi3/TestCaseRepository.java | 40 ++++-- .../TestCaseResolutionStatusRepository.java | 67 +++++++--- .../migration/mysql/v140/Migration.java | 5 + .../migration/postgres/v140/Migration.java | 5 + .../migration/utils/v140/MigrationUtil.java | 55 +++++++++ .../resources/dqtests/TestCaseResource.java | 14 ++- .../service/search/SearchRepository.java | 20 +++ ..._case_resolution_status_index_mapping.json | 53 +++++++- ..._case_resolution_status_index_mapping.json | 53 +++++++- ..._case_resolution_status_index_mapping.json | 53 +++++++- .../dqtests/TestCaseResourceTest.java | 19 +++ .../DataQualityTab/DataQualityTab.tsx | 1 + 19 files changed, 492 insertions(+), 44 deletions(-) diff --git a/ingestion/src/metadata/data_quality/validations/column/sqlalchemy/columnValuesToBeUnique.py b/ingestion/src/metadata/data_quality/validations/column/sqlalchemy/columnValuesToBeUnique.py index 4f5080d59eb..1765a4404d3 100644 --- a/ingestion/src/metadata/data_quality/validations/column/sqlalchemy/columnValuesToBeUnique.py +++ b/ingestion/src/metadata/data_quality/validations/column/sqlalchemy/columnValuesToBeUnique.py @@ -63,7 +63,7 @@ class ColumnValuesToBeUniqueValidator( ) # type: ignore try: - self.value = dict(self.runner.dispatch_query_select_first(count, unique_count.subquery("uniqueCount"))) # type: ignore + self.value = dict(self.runner.dispatch_query_select_first(count, unique_count.scalar_subquery().label("uniqueCount"))) # type: ignore res = self.value.get(Metrics.COUNT.name) except Exception as exc: raise SQLAlchemyError(exc) diff --git a/ingestion/tests/utils/docker_service_builders/database_container/mysql_test_container.py b/ingestion/tests/utils/docker_service_builders/database_container/mysql_test_container.py index 49766d9833d..8338114ae3e 100644 --- a/ingestion/tests/utils/docker_service_builders/database_container/mysql_test_container.py +++ b/ingestion/tests/utils/docker_service_builders/database_container/mysql_test_container.py @@ -20,6 +20,7 @@ from .database_test_container import DataBaseTestContainer class MySQLTestContainer(DataBaseTestContainer): def __init__(self): self.mysql_container = MySqlContainer("mysql:latest") + self.mysql_container.with_env("TC_POOLING_INTERVAL", "3") self.start() self.connection_url = self.mysql_container.get_connection_url() super().__init__() diff --git a/ingestion/tests/utils/docker_service_builders/database_container/postgres_test_container.py b/ingestion/tests/utils/docker_service_builders/database_container/postgres_test_container.py index 163d2972f4a..abcf1de11e1 100644 --- a/ingestion/tests/utils/docker_service_builders/database_container/postgres_test_container.py +++ b/ingestion/tests/utils/docker_service_builders/database_container/postgres_test_container.py @@ -20,6 +20,7 @@ from .database_test_container import DataBaseTestContainer class PostgresTestContainer(DataBaseTestContainer): def __init__(self): self.postgres_container = PostgresContainer("postgres:latest") + self.postgres_container.with_env("TC_POOLING_INTERVAL", "3") self.start() self.connection_url = self.postgres_container.get_connection_url() super().__init__() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java index afae8213813..72a7d8982ed 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java @@ -1081,7 +1081,13 @@ public abstract class EntityRepository { throw new IllegalArgumentException(CatalogExceptionMessage.entityIsNotEmpty(entityType)); } // Delete all the contained entities - for (EntityRelationshipRecord entityRelationshipRecord : childrenRecords) { + deleteChildren(childrenRecords, hardDelete, updatedBy); + } + + @Transaction + protected void deleteChildren( + List children, boolean hardDelete, String updatedBy) { + for (EntityRelationshipRecord entityRelationshipRecord : children) { LOG.info( "Recursively {} deleting {} {}", hardDelete ? "hard" : "soft", @@ -1581,7 +1587,7 @@ public abstract class EntityRepository { : null; } - public final void ensureSingleRelationship( + public static void ensureSingleRelationship( String entityType, UUID id, List relations, diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesDAO.java index e9212ea4134..f6f2520a3f4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesDAO.java @@ -235,6 +235,13 @@ public interface EntityTimeSeriesDAO { return getById(getTimeSeriesTableName(), id.toString()); } + @SqlUpdate(value = "DELETE from WHERE id = :id") + void deleteById(@Define("table") String table, @Bind("id") String id); + + default void deleteById(UUID id) { + deleteById(getTimeSeriesTableName(), id.toString()); + } + /** @deprecated */ @SqlQuery("SELECT COUNT(DISTINCT entityFQN) FROM
") @Deprecated(since = "1.1.1") diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java index a24572de4ef..9cabcbb1004 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java @@ -1,11 +1,15 @@ package org.openmetadata.service.jdbi3; +import static org.openmetadata.schema.type.Include.ALL; + import java.util.ArrayList; import java.util.List; import java.util.UUID; import lombok.Getter; import org.jdbi.v3.sqlobject.transaction.Transaction; import org.openmetadata.schema.EntityTimeSeriesInterface; +import org.openmetadata.schema.type.EntityReference; +import org.openmetadata.schema.type.Relationship; import org.openmetadata.service.Entity; import org.openmetadata.service.search.SearchRepository; import org.openmetadata.service.util.JsonUtils; @@ -38,21 +42,91 @@ public abstract class EntityTimeSeriesRepository 0) { + // For bidirectional relationship, instead of adding two row fromId -> toId and toId -> + // fromId, just add one row where fromId is alphabetically less than toId + from = toId; + to = fromId; + } + daoCollection + .relationshipDAO() + .insert(from, to, fromEntity, toEntity, relationship.ordinal(), json); + } + + protected void postCreate(T recordEntity) { + searchRepository.createTimeSeriesEntity(JsonUtils.deepCopy(recordEntity, entityClass)); + } + + protected void postDelete(T recordEntity) { + searchRepository.deleteTimeSeriesEntityById(JsonUtils.deepCopy(recordEntity, entityClass)); + } + + public final List findFromRecords( + UUID toId, String toEntityType, Relationship relationship, String fromEntityType) { + // When fromEntityType is null, all the relationships from any entity is returned + return fromEntityType == null + ? daoCollection.relationshipDAO().findFrom(toId, toEntityType, relationship.ordinal()) + : daoCollection + .relationshipDAO() + .findFrom(toId, toEntityType, relationship.ordinal(), fromEntityType); + } + + protected EntityReference getFromEntityRef( + UUID toId, Relationship relationship, String fromEntityType, boolean mustHaveRelationship) { + List records = + findFromRecords(toId, entityType, relationship, fromEntityType); + EntityRepository.ensureSingleRelationship( + entityType, toId, records, relationship.value(), fromEntityType, mustHaveRelationship); + return !records.isEmpty() + ? Entity.getEntityReferenceById(records.get(0).getType(), records.get(0).getId(), ALL) + : null; } public final ResultList getResultList( @@ -90,8 +164,9 @@ public abstract class EntityTimeSeriesRepository { } } - // If we delete the test case, we need to clean up the resolution ts - daoCollection.testCaseResolutionStatusTimeSeriesDao().delete(test.getFullyQualifiedName()); - deleteTestCaseFailedRowsSample(test.getId()); } @@ -372,13 +369,34 @@ public class TestCaseRepository extends EntityRepository { .withUpdatedAt(System.currentTimeMillis()) .withTestCaseReference(testCase.getEntityReference()); + testCaseResolutionStatusRepository.createNewRecord(status, testCase.getFullyQualifiedName()); TestCaseResolutionStatus incident = - testCaseResolutionStatusRepository.createNewRecord( - status, testCase.getFullyQualifiedName()); + testCaseResolutionStatusRepository.getLatestRecord(testCase.getFullyQualifiedName()); return incident.getStateId(); } + @Transaction + @Override + protected void deleteChildren( + List children, boolean hardDelete, String updatedBy) { + if (hardDelete) { + for (CollectionDAO.EntityRelationshipRecord entityRelationshipRecord : children) { + LOG.info( + "Recursively {} deleting {} {}", + hardDelete ? "hard" : "soft", + entityRelationshipRecord.getType(), + entityRelationshipRecord.getId()); + TestCaseResolutionStatusRepository testCaseResolutionStatusRepository = + (TestCaseResolutionStatusRepository) + Entity.getEntityTimeSeriesRepository(Entity.TEST_CASE_RESOLUTION_STATUS); + for (CollectionDAO.EntityRelationshipRecord child : children) { + testCaseResolutionStatusRepository.deleteById(child.getId(), hardDelete); + } + } + } + } + public RestUtil.PutResponse deleteTestCaseResult( String updatedBy, String fqn, Long timestamp) { // Validate the request content @@ -778,12 +796,16 @@ public class TestCaseRepository extends EntityRepository { .withTestCaseReference(latestTestCaseResolutionStatus.getTestCaseReference()) .withUpdatedBy(user.getEntityReference()); + EntityReference testCaseReference = testCaseResolutionStatus.getTestCaseReference(); + testCaseResolutionStatus.setTestCaseReference(null); Entity.getCollectionDAO() .testCaseResolutionStatusTimeSeriesDao() .insert( - testCaseResolutionStatus.getTestCaseReference().getFullyQualifiedName(), + testCaseReference.getFullyQualifiedName(), Entity.TEST_CASE_RESOLUTION_STATUS, JsonUtils.pojoToJson(testCaseResolutionStatus)); + testCaseResolutionStatus.setTestCaseReference(testCaseReference); + testCaseResolutionStatusRepository.storeRelationship(testCaseResolutionStatus); testCaseResolutionStatusRepository.postCreate(testCaseResolutionStatus); // Return the TestCase with the StateId to avoid any unnecessary PATCH when resolving the task @@ -833,12 +855,16 @@ public class TestCaseRepository extends EntityRepository { .withTestCaseReference(latestTestCaseResolutionStatus.getTestCaseReference()) .withUpdatedBy(user.getEntityReference()); + EntityReference testCaseReference = testCaseResolutionStatus.getTestCaseReference(); + testCaseResolutionStatus.setTestCaseReference(null); Entity.getCollectionDAO() .testCaseResolutionStatusTimeSeriesDao() .insert( - testCaseResolutionStatus.getTestCaseReference().getFullyQualifiedName(), + testCaseReference.getFullyQualifiedName(), Entity.TEST_CASE_RESOLUTION_STATUS, JsonUtils.pojoToJson(testCaseResolutionStatus)); + testCaseResolutionStatus.setTestCaseReference(testCaseReference); + testCaseResolutionStatusRepository.storeRelationship(testCaseResolutionStatus); testCaseResolutionStatusRepository.postCreate(testCaseResolutionStatus); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestCaseResolutionStatusRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestCaseResolutionStatusRepository.java index 30a6ad5f9b7..176cddcf2ad 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestCaseResolutionStatusRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TestCaseResolutionStatusRepository.java @@ -7,6 +7,7 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -14,6 +15,7 @@ import javax.json.JsonPatch; import javax.ws.rs.core.Response; import org.jdbi.v3.sqlobject.transaction.Transaction; import org.openmetadata.schema.EntityInterface; +import org.openmetadata.schema.api.feed.CloseTask; import org.openmetadata.schema.api.feed.ResolveTask; import org.openmetadata.schema.entity.feed.Thread; import org.openmetadata.schema.entity.teams.User; @@ -25,6 +27,7 @@ import org.openmetadata.schema.tests.type.TestCaseResolutionStatus; import org.openmetadata.schema.tests.type.TestCaseResolutionStatusTypes; import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.Include; +import org.openmetadata.schema.type.Relationship; import org.openmetadata.schema.type.TaskDetails; import org.openmetadata.schema.type.TaskStatus; import org.openmetadata.schema.type.TaskType; @@ -53,11 +56,17 @@ public class TestCaseResolutionStatusRepository public ResultList listTestCaseResolutionStatusesForStateId( UUID stateId) { + List testCaseResolutionStatuses = new ArrayList<>(); List jsons = ((CollectionDAO.TestCaseResolutionStatusTimeSeriesDAO) timeSeriesDao) .listTestCaseResolutionStatusesForStateId(stateId.toString()); - List testCaseResolutionStatuses = - JsonUtils.readObjects(jsons, TestCaseResolutionStatus.class); + + for (String json : jsons) { + TestCaseResolutionStatus testCaseResolutionStatus = + JsonUtils.readValue(json, TestCaseResolutionStatus.class); + setInheritedFields(testCaseResolutionStatus); + testCaseResolutionStatuses.add(testCaseResolutionStatus); + } return getResultList(testCaseResolutionStatuses, null, null, testCaseResolutionStatuses.size()); } @@ -145,11 +154,9 @@ public class TestCaseResolutionStatusRepository @Override @Transaction - public TestCaseResolutionStatus createNewRecord( - TestCaseResolutionStatus recordEntity, String recordFQN) { + public void storeInternal(TestCaseResolutionStatus recordEntity, String recordFQN) { - TestCaseResolutionStatus lastIncident = - getLatestRecord(recordEntity.getTestCaseReference().getFullyQualifiedName()); + TestCaseResolutionStatus lastIncident = getLatestRecord(recordFQN); if (recordEntity.getStateId() == null) { recordEntity.setStateId(UUID.randomUUID()); @@ -177,7 +184,7 @@ public class TestCaseResolutionStatusRepository case New -> { // If there is already an existing New incident we'll return it if (Boolean.TRUE.equals(unresolvedIncident(lastIncident))) { - return lastIncident; + return; } } case Ack, Assigned -> openOrAssignTask(recordEntity); @@ -187,12 +194,33 @@ public class TestCaseResolutionStatusRepository // We don't create a new record. The new status will be added via the // TestCaseFailureResolutionTaskWorkflow // implemented in the TestCaseRepository. - return getLatestRecord(recordEntity.getTestCaseReference().getFullyQualifiedName()); + return; } default -> throw new IllegalArgumentException( String.format("Invalid status %s", recordEntity.getTestCaseResolutionStatusType())); } - return super.createNewRecord(recordEntity, recordFQN); + EntityReference testCaseReference = recordEntity.getTestCaseReference(); + recordEntity.withTestCaseReference(null); // we don't want to store the reference in the record + super.storeInternal(recordEntity, recordFQN); + recordEntity.withTestCaseReference(testCaseReference); + } + + @Override + protected void storeRelationship(TestCaseResolutionStatus recordEntity) { + addRelationship( + recordEntity.getTestCaseReference().getId(), + recordEntity.getId(), + Entity.TEST_CASE, + Entity.TEST_CASE_RESOLUTION_STATUS, + Relationship.PARENT_OF, + null, + false); + } + + @Override + protected void setInheritedFields(TestCaseResolutionStatus recordEntity) { + recordEntity.setTestCaseReference( + getFromEntityRef(recordEntity.getId(), Relationship.PARENT_OF, Entity.TEST_CASE, true)); } private void openOrAssignTask(TestCaseResolutionStatus incidentStatus) { @@ -253,17 +281,20 @@ public class TestCaseResolutionStatusRepository Thread thread = getIncidentTask(lastIncidentStatus); if (thread != null) { - // If there is an existing task, we'll resolve it and create a new incident - // status with the Resolved status flow + // If there is an existing task, we'll close it without performing the workflow + // (i.e. creating a new incident which will be handled here). + FeedRepository.ThreadContext threadContext = new FeedRepository.ThreadContext(thread); + threadContext.getThread().getTask().withNewValue(resolveTask.getNewValue()); Entity.getFeedRepository() - .resolveTask( - new FeedRepository.ThreadContext(thread), - updatedBy.getFullyQualifiedName(), - resolveTask); - } else { - // if there is no task, we'll simply create a new incident status (e.g. New -> Resolved) - super.createNewRecord(newIncidentStatus, testCase.getFullyQualifiedName()); + .closeTaskWithoutWorkflow( + threadContext.getThread(), updatedBy.getFullyQualifiedName(), new CloseTask()); } + // if there is no task, we'll simply create a new incident status (e.g. New -> Resolved) + EntityReference testCaseReference = newIncidentStatus.getTestCaseReference(); + newIncidentStatus.setTestCaseReference( + null); // we don't want to store the reference in the record + super.storeInternal(newIncidentStatus, testCase.getFullyQualifiedName()); + newIncidentStatus.setTestCaseReference(testCaseReference); } private void createTask( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v140/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v140/Migration.java index 42019949fae..1d2a95dd295 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v140/Migration.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v140/Migration.java @@ -2,6 +2,7 @@ package org.openmetadata.service.migration.mysql.v140; import static org.openmetadata.service.migration.utils.v140.MigrationUtil.migrateGenericToWebhook; import static org.openmetadata.service.migration.utils.v140.MigrationUtil.migrateTablePartition; +import static org.openmetadata.service.migration.utils.v140.MigrationUtil.migrateTestCaseResolution; import lombok.SneakyThrows; import org.jdbi.v3.core.Handle; @@ -27,9 +28,13 @@ public class Migration extends MigrationProcessImpl { @Override @SneakyThrows public void runDataMigration() { + // Migrate Table Partition migrateTablePartition(handle, collectionDAO); // Migrate Generic to Webhook migrateGenericToWebhook(collectionDAO); + + // Migrate Test case resolution status + migrateTestCaseResolution(handle, collectionDAO); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v140/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v140/Migration.java index 8e56a1f1476..cb38f869bc3 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v140/Migration.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v140/Migration.java @@ -2,6 +2,7 @@ package org.openmetadata.service.migration.postgres.v140; import static org.openmetadata.service.migration.utils.v140.MigrationUtil.migrateGenericToWebhook; import static org.openmetadata.service.migration.utils.v140.MigrationUtil.migrateTablePartition; +import static org.openmetadata.service.migration.utils.v140.MigrationUtil.migrateTestCaseResolution; import lombok.SneakyThrows; import org.jdbi.v3.core.Handle; @@ -27,9 +28,13 @@ public class Migration extends MigrationProcessImpl { @Override @SneakyThrows public void runDataMigration() { + // Migrate Table Partition migrateTablePartition(handle, collectionDAO); // Migrate Generic to Webhook migrateGenericToWebhook(collectionDAO); + + // Migrate Test case resolution status + migrateTestCaseResolution(handle, collectionDAO); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v140/MigrationUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v140/MigrationUtil.java index ce91265c5ed..217af9fda16 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v140/MigrationUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v140/MigrationUtil.java @@ -3,6 +3,7 @@ package org.openmetadata.service.migration.utils.v140; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.UUID; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonArrayBuilder; @@ -16,9 +17,12 @@ import org.json.JSONObject; import org.openmetadata.schema.api.services.CreateDatabaseService; import org.openmetadata.schema.entity.data.Table; import org.openmetadata.schema.entity.events.EventSubscription; +import org.openmetadata.schema.tests.type.TestCaseResolutionStatus; import org.openmetadata.schema.type.PartitionColumnDetails; import org.openmetadata.schema.type.PartitionIntervalTypes; +import org.openmetadata.schema.type.Relationship; import org.openmetadata.schema.type.TablePartition; +import org.openmetadata.service.Entity; import org.openmetadata.service.jdbi3.CollectionDAO; import org.openmetadata.service.resources.databases.DatasourceConfig; import org.openmetadata.service.util.JsonUtils; @@ -34,6 +38,13 @@ public class MigrationUtil { private static final String POSTGRES_QUERY_TABLES_WITH_PARTITION = "SELECT json " + "FROM table_entity " + "WHERE json->'tablePartition' IS NOT NULL"; + private static final String TEST_CASE_RESOLUTION_QUERY = + "SELECT json FROM test_case_resolution_status_time_series"; + private static final String MYSQL_TEST_CASE_RESOLUTION_UPDATE_QUERY = + "UPDATE test_case_resolution_status_time_series SET json = :json WHERE id = :id"; + private static final String POSTGRES_TEST_CASE_RESOLUTION_UPDATE_QUERY = + "UPDATE test_case_resolution_status_time_series SET json = :json::jsonb WHERE id = :id"; + private MigrationUtil() { /* Cannot create object util class*/ } @@ -64,6 +75,50 @@ public class MigrationUtil { } } + public static void migrateTestCaseResolution(Handle handle, CollectionDAO collectionDAO) { + try { + handle + .createQuery(TEST_CASE_RESOLUTION_QUERY) + .mapToMap() + .forEach( + row -> { + try { + TestCaseResolutionStatus testCaseResolutionStatus = + JsonUtils.readValue( + row.get("json").toString(), TestCaseResolutionStatus.class); + UUID fromId = testCaseResolutionStatus.getTestCaseReference().getId(); + UUID toId = testCaseResolutionStatus.getId(); + // Store the test case <-> incident relationship + collectionDAO + .relationshipDAO() + .insert( + fromId, + toId, + Entity.TEST_CASE, + Entity.TEST_CASE_RESOLUTION_STATUS, + Relationship.PARENT_OF.ordinal(), + null); + // Remove the test case reference from the test case resolution status + testCaseResolutionStatus.setTestCaseReference(null); + String json = JsonUtils.pojoToJson(testCaseResolutionStatus); + String updateQuery = MYSQL_TEST_CASE_RESOLUTION_UPDATE_QUERY; + if (Boolean.FALSE.equals(DatasourceConfig.getInstance().isMySQL())) { + updateQuery = POSTGRES_TEST_CASE_RESOLUTION_UPDATE_QUERY; + } + handle + .createUpdate(updateQuery) + .bind("json", json) + .bind("id", toId.toString()) + .execute(); + } catch (Exception ex) { + LOG.warn("Error during the test case resolution migration due to ", ex); + } + }); + } catch (Exception ex) { + LOG.warn("Error running the test case resolution migration ", ex); + } + } + public static void migrateTablePartition(Handle handle, CollectionDAO collectionDAO) { try { if (Boolean.TRUE.equals(DatasourceConfig.getInstance().isMySQL())) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java index 20646712891..799c357b17b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java @@ -737,6 +737,11 @@ public class TestCaseResource extends EntityResource t.getTestCaseReference().getId().equals(testCaseEntity1.getId()))); + + deleteEntity(testCaseEntity1.getId(), true, true, ADMIN_AUTH_HEADERS); + storedTestCaseResolutions = + getTestCaseFailureStatus(startTs, endTs, null, TestCaseResolutionStatusTypes.Ack); + assertEquals(1, storedTestCaseResolutions.getData().size()); + assertTrue( + storedTestCaseResolutions.getData().stream() + .noneMatch(t -> t.getTestCaseReference().getId().equals(testCaseEntity1.getId()))); } @Test diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx index c2157167c1d..c5f2acf991b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/DataQualityTab/DataQualityTab.tsx @@ -485,6 +485,7 @@ const DataQualityTab: React.FC = ({ /> ) : (