mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-11-04 04:29:13 +00:00 
			
		
		
		
	MINOR - Test Case Ownership, keep severity, add test case result incidentId (#14691)
* MINOR - Test Case Ownership, keep severity, add test case result incidentId * fix immutability * Fix tests * MINOR - Test Case Ownership, keep severity, add test case result incidentId --------- Co-authored-by: Pere Miquel Brull <peremiquelbrull@gmail.com>
This commit is contained in:
		
							parent
							
								
									a7c17c5351
								
							
						
					
					
						commit
						49c1756583
					
				@ -2,6 +2,7 @@ package org.openmetadata.service.jdbi3;
 | 
			
		||||
 | 
			
		||||
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
 | 
			
		||||
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
 | 
			
		||||
import static org.openmetadata.schema.type.Include.ALL;
 | 
			
		||||
import static org.openmetadata.service.Entity.TEST_CASE;
 | 
			
		||||
import static org.openmetadata.service.Entity.TEST_DEFINITION;
 | 
			
		||||
import static org.openmetadata.service.Entity.TEST_SUITE;
 | 
			
		||||
@ -23,6 +24,7 @@ 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.data.Table;
 | 
			
		||||
import org.openmetadata.schema.entity.teams.User;
 | 
			
		||||
import org.openmetadata.schema.tests.ResultSummary;
 | 
			
		||||
import org.openmetadata.schema.tests.TestCase;
 | 
			
		||||
@ -89,6 +91,18 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
 | 
			
		||||
        fields.contains(INCIDENTS_FIELD) ? getIncidentId(test) : test.getIncidentId());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setInheritedFields(TestCase testCase, Fields fields) {
 | 
			
		||||
    EntityLink entityLink = EntityLink.parse(testCase.getEntityLink());
 | 
			
		||||
    Table table = Entity.getEntity(entityLink, "owner", ALL);
 | 
			
		||||
    inheritOwner(testCase, fields, table);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public EntityInterface getParentEntity(TestCase entity, String fields) {
 | 
			
		||||
    return Entity.getEntity(entity.getTestSuite(), fields, Include.NON_DELETED);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void clearFields(TestCase test, Fields fields) {
 | 
			
		||||
    test.setTestSuites(fields.contains("testSuites") ? test.getTestSuites() : null);
 | 
			
		||||
@ -249,8 +263,14 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
 | 
			
		||||
    UUID incidentStateId = null;
 | 
			
		||||
    if (TestCaseStatus.Failed.equals(testCaseResult.getTestCaseStatus())) {
 | 
			
		||||
      incidentStateId = getOrCreateIncidentOnFailure(testCase, updatedBy);
 | 
			
		||||
      // Set the incident ID to the test case result to ensure we can link result <> incident when
 | 
			
		||||
      // plotting the UI
 | 
			
		||||
      // even after the incident has been closed.
 | 
			
		||||
      testCaseResult.setIncidentId(incidentStateId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We add the incidentStateId in the DQ table to quickly link Test Case <> Incident
 | 
			
		||||
    // When we Resolve the incident, we'll clean up this incidentId column
 | 
			
		||||
    daoCollection
 | 
			
		||||
        .dataQualityDataTimeSeriesDao()
 | 
			
		||||
        .insert(
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,6 @@ 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;
 | 
			
		||||
@ -133,11 +132,9 @@ public class TestCaseResolutionStatusRepository
 | 
			
		||||
          throw IncidentManagerException.invalidStatus(lastStatus, newStatus);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      case Resolved -> {
 | 
			
		||||
        if (!newStatus.equals(TestCaseResolutionStatusTypes.Resolved)) {
 | 
			
		||||
          throw IncidentManagerException.invalidStatus(lastStatus, newStatus);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
        // We only validate status if the last one is unresolved, so we should
 | 
			
		||||
        // never land here
 | 
			
		||||
      default -> throw IncidentManagerException.invalidStatus(lastStatus, newStatus);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -156,29 +153,36 @@ public class TestCaseResolutionStatusRepository
 | 
			
		||||
    // if we have an ongoing incident, set the stateId if the new record to be created
 | 
			
		||||
    // and validate the flow
 | 
			
		||||
    if (Boolean.TRUE.equals(unresolvedIncident(lastIncident))) {
 | 
			
		||||
      recordEntity.setStateId(lastIncident.getStateId());
 | 
			
		||||
      validateStatus(
 | 
			
		||||
          lastIncident.getTestCaseResolutionStatusType(),
 | 
			
		||||
          recordEntity.getTestCaseResolutionStatusType());
 | 
			
		||||
      // If there is an unresolved incident update the state ID
 | 
			
		||||
      recordEntity.setStateId(lastIncident.getStateId());
 | 
			
		||||
      // If the last incident had a severity assigned and the incoming incident does not, inherit
 | 
			
		||||
      // the old severity
 | 
			
		||||
      recordEntity.setSeverity(
 | 
			
		||||
          recordEntity.getSeverity() == null
 | 
			
		||||
              ? lastIncident.getSeverity()
 | 
			
		||||
              : recordEntity.getSeverity());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (recordEntity.getTestCaseResolutionStatusType()) {
 | 
			
		||||
        // When we create a NEW incident, we need to open a task with the test case owner as the
 | 
			
		||||
        // assignee. We don't need to check any past history
 | 
			
		||||
      case New -> {
 | 
			
		||||
        // If there is already an unresolved incident, return it without doing any
 | 
			
		||||
        // further logic.
 | 
			
		||||
        if (Boolean.TRUE.equals(unresolvedIncident(lastIncident))) {
 | 
			
		||||
          return getLatestRecord(lastIncident.getTestCaseReference().getFullyQualifiedName());
 | 
			
		||||
        }
 | 
			
		||||
        // When we create a NEW incident, we need to open a task with the test case owner or
 | 
			
		||||
        // the table owner as the assignee.
 | 
			
		||||
        openNewTask(recordEntity);
 | 
			
		||||
      }
 | 
			
		||||
      case Ack -> {
 | 
			
		||||
        /* nothing to do for ACK. The Owner already has the task open. It will close it when reassigning it */
 | 
			
		||||
        /* nothing to do for ACK. The Owner already has the task open */
 | 
			
		||||
      }
 | 
			
		||||
      case Assigned -> assignTask(recordEntity, lastIncident);
 | 
			
		||||
        // When the incident is Resolved, we will close the Assigned task.
 | 
			
		||||
      case Resolved -> {
 | 
			
		||||
        // When the incident is Resolved, we will close the Assigned task.
 | 
			
		||||
        resolveTask(recordEntity, lastIncident);
 | 
			
		||||
        // We don't create a new record. The new status will be added via the
 | 
			
		||||
        // TestCaseFailureResolutionTaskWorkflow
 | 
			
		||||
@ -193,16 +197,10 @@ public class TestCaseResolutionStatusRepository
 | 
			
		||||
 | 
			
		||||
  private void openNewTask(TestCaseResolutionStatus incidentStatus) {
 | 
			
		||||
 | 
			
		||||
    List<EntityReference> owners =
 | 
			
		||||
        EntityUtil.getEntityReferences(
 | 
			
		||||
            daoCollection
 | 
			
		||||
                .relationshipDAO()
 | 
			
		||||
                .findFrom(
 | 
			
		||||
                    incidentStatus.getTestCaseReference().getId(),
 | 
			
		||||
                    Entity.TEST_CASE,
 | 
			
		||||
                    Relationship.OWNS.ordinal(),
 | 
			
		||||
                    Entity.USER));
 | 
			
		||||
    createTask(incidentStatus, owners, "New Incident");
 | 
			
		||||
    TestCase testCase =
 | 
			
		||||
        Entity.getEntity(incidentStatus.getTestCaseReference(), "owner", Include.NON_DELETED);
 | 
			
		||||
 | 
			
		||||
    createTask(incidentStatus, Collections.singletonList(testCase.getOwner()), "New Incident");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void assignTask(
 | 
			
		||||
 | 
			
		||||
@ -1480,7 +1480,9 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
 | 
			
		||||
    // Create entity without description, owner
 | 
			
		||||
    T entity = createEntity(createRequest(getEntityName(test), "", null, null), ADMIN_AUTH_HEADERS);
 | 
			
		||||
    // user will always have the same user assigned as the owner
 | 
			
		||||
    if (!Entity.getEntityTypeFromObject(entity).equals(Entity.USER)) {
 | 
			
		||||
    if (!Entity.getEntityTypeFromObject(entity).equals(Entity.USER)
 | 
			
		||||
        && entity.getOwner() != null
 | 
			
		||||
        && !entity.getOwner().getInherited()) {
 | 
			
		||||
      assertListNull(entity.getOwner());
 | 
			
		||||
    }
 | 
			
		||||
    entity = getEntity(entity.getId(), ADMIN_AUTH_HEADERS);
 | 
			
		||||
@ -2898,6 +2900,11 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
 | 
			
		||||
 | 
			
		||||
  /** Compare entity Id and types in the entityReference */
 | 
			
		||||
  protected static void assertReference(EntityReference expected, EntityReference actual) {
 | 
			
		||||
    // If the actual value is inherited, it will never match the expected
 | 
			
		||||
    // We just ignore the validation in these cases
 | 
			
		||||
    if (actual != null && actual.getInherited() != null && actual.getInherited()) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (expected != null) {
 | 
			
		||||
      assertNotNull(actual);
 | 
			
		||||
      TestUtils.validateEntityReference(actual);
 | 
			
		||||
@ -3170,7 +3177,12 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr
 | 
			
		||||
  public static EntityReference reduceEntityReference(EntityReference ref) {
 | 
			
		||||
    // In requests send minimum entity reference information to ensure the server fills rest of the
 | 
			
		||||
    // details
 | 
			
		||||
    return ref != null ? new EntityReference().withType(ref.getType()).withId(ref.getId()) : null;
 | 
			
		||||
    return ref != null && (ref.getInherited() == null || !ref.getInherited())
 | 
			
		||||
        ? new EntityReference()
 | 
			
		||||
            .withType(ref.getType())
 | 
			
		||||
            .withId(ref.getId())
 | 
			
		||||
            .withInherited(ref.getInherited())
 | 
			
		||||
        : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public String getAllowedFields() {
 | 
			
		||||
 | 
			
		||||
@ -1610,6 +1610,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
 | 
			
		||||
    assertEquals(expectedTestCaseResults.size(), actualTestCaseResults.getData().size());
 | 
			
		||||
    Map<Long, TestCaseResult> testCaseResultMap = new HashMap<>();
 | 
			
		||||
    for (TestCaseResult result : actualTestCaseResults.getData()) {
 | 
			
		||||
      result.setIncidentId(null);
 | 
			
		||||
      testCaseResultMap.put(result.getTimestamp(), result);
 | 
			
		||||
    }
 | 
			
		||||
    for (TestCaseResult result : expectedTestCaseResults) {
 | 
			
		||||
 | 
			
		||||
@ -108,6 +108,10 @@
 | 
			
		||||
        "failedRowsPercentage": {
 | 
			
		||||
          "description": "Percentage of rows that failed.",
 | 
			
		||||
          "type": "number"
 | 
			
		||||
        },
 | 
			
		||||
        "incidentId": {
 | 
			
		||||
          "description": "Incident State ID associated with this result. This association happens when the result is created, and will stay there even when the incident is resolved.",
 | 
			
		||||
          "$ref": "../type/basic.json#/definitions/uuid"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user