mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-24 14:45:55 +00:00
MINOR: Update Incident Manager Flow
This commit is contained in:
parent
abe716d7fa
commit
085dfc0012
@ -3708,12 +3708,6 @@ public interface CollectionDAO {
|
|||||||
return "data_quality_data_time_series";
|
return "data_quality_data_time_series";
|
||||||
}
|
}
|
||||||
|
|
||||||
@SqlQuery(
|
|
||||||
value =
|
|
||||||
"SELECT DISTINCT incidentId FROM data_quality_data_time_series "
|
|
||||||
+ "WHERE entityFQNHash = :testCaseFQNHash AND incidentId IS NOT NULL")
|
|
||||||
List<String> getResultsWithIncidents(@BindFQN("testCaseFQNHash") String testCaseFQNHash);
|
|
||||||
|
|
||||||
@SqlUpdate(
|
@SqlUpdate(
|
||||||
value =
|
value =
|
||||||
"UPDATE data_quality_data_time_series SET incidentId = NULL "
|
"UPDATE data_quality_data_time_series SET incidentId = NULL "
|
||||||
|
|||||||
@ -38,11 +38,15 @@ public abstract class EntityTimeSeriesRepository<T extends EntityTimeSeriesInter
|
|||||||
@Transaction
|
@Transaction
|
||||||
public T createNewRecord(T recordEntity, String extension, String recordFQN) {
|
public T createNewRecord(T recordEntity, String extension, String recordFQN) {
|
||||||
recordEntity.setId(UUID.randomUUID());
|
recordEntity.setId(UUID.randomUUID());
|
||||||
if (extension != null) {
|
timeSeriesDao.insert(recordFQN, extension, entityType, JsonUtils.pojoToJson(recordEntity));
|
||||||
timeSeriesDao.insert(recordFQN, extension, entityType, JsonUtils.pojoToJson(recordEntity));
|
postCreate(recordEntity);
|
||||||
} else {
|
return recordEntity;
|
||||||
timeSeriesDao.insert(recordFQN, entityType, JsonUtils.pojoToJson(recordEntity));
|
}
|
||||||
}
|
|
||||||
|
@Transaction
|
||||||
|
public T createNewRecord(T recordEntity, String recordFQN) {
|
||||||
|
recordEntity.setId(UUID.randomUUID());
|
||||||
|
timeSeriesDao.insert(recordFQN, entityType, JsonUtils.pojoToJson(recordEntity));
|
||||||
postCreate(recordEntity);
|
postCreate(recordEntity);
|
||||||
return recordEntity;
|
return recordEntity;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.json.JsonPatch;
|
import javax.json.JsonPatch;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
@ -281,7 +282,8 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
|
|||||||
JsonUtils.pojoToJson(testCaseResult),
|
JsonUtils.pojoToJson(testCaseResult),
|
||||||
incidentStateId != null ? incidentStateId.toString() : null);
|
incidentStateId != null ? incidentStateId.toString() : null);
|
||||||
|
|
||||||
setFieldsInternal(testCase, new EntityUtil.Fields(allowedFields, TEST_SUITE_FIELD));
|
setFieldsInternal(
|
||||||
|
testCase, new EntityUtil.Fields(allowedFields, Set.of(TEST_SUITE_FIELD, INCIDENTS_FIELD)));
|
||||||
setTestSuiteSummary(
|
setTestSuiteSummary(
|
||||||
testCase, testCaseResult.getTimestamp(), testCaseResult.getTestCaseStatus(), false);
|
testCase, testCaseResult.getTimestamp(), testCaseResult.getTestCaseStatus(), false);
|
||||||
setTestCaseResult(testCase, testCaseResult, false);
|
setTestCaseResult(testCase, testCaseResult, false);
|
||||||
@ -326,7 +328,7 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
|
|||||||
|
|
||||||
TestCaseResolutionStatus incident =
|
TestCaseResolutionStatus incident =
|
||||||
testCaseResolutionStatusRepository.createNewRecord(
|
testCaseResolutionStatusRepository.createNewRecord(
|
||||||
status, null, testCase.getFullyQualifiedName());
|
status, testCase.getFullyQualifiedName());
|
||||||
|
|
||||||
return incident.getStateId();
|
return incident.getStateId();
|
||||||
}
|
}
|
||||||
@ -538,16 +540,12 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
|
|||||||
private UUID getIncidentId(TestCase test) {
|
private UUID getIncidentId(TestCase test) {
|
||||||
UUID ongoingIncident = null;
|
UUID ongoingIncident = null;
|
||||||
|
|
||||||
List<UUID> incidents =
|
String json =
|
||||||
daoCollection
|
daoCollection.dataQualityDataTimeSeriesDao().getLatestRecord(test.getFullyQualifiedName());
|
||||||
.dataQualityDataTimeSeriesDao()
|
TestCaseResult latestTestCaseResult = JsonUtils.readValue(json, TestCaseResult.class);
|
||||||
.getResultsWithIncidents(test.getFullyQualifiedName())
|
|
||||||
.stream()
|
|
||||||
.map(UUID::fromString)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (!nullOrEmpty(incidents)) {
|
if (!nullOrEmpty(latestTestCaseResult)) {
|
||||||
ongoingIncident = incidents.get(0);
|
ongoingIncident = latestTestCaseResult.getIncidentId();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ongoingIncident;
|
return ongoingIncident;
|
||||||
@ -792,12 +790,6 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
|
|||||||
JsonUtils.pojoToJson(testCaseResolutionStatus));
|
JsonUtils.pojoToJson(testCaseResolutionStatus));
|
||||||
testCaseResolutionStatusRepository.postCreate(testCaseResolutionStatus);
|
testCaseResolutionStatusRepository.postCreate(testCaseResolutionStatus);
|
||||||
|
|
||||||
// When we resolve a task, we clean up the test case results associated
|
|
||||||
// with the resolved stateId
|
|
||||||
dataQualityDataTimeSeriesDao.cleanTestCaseIncident(
|
|
||||||
latestTestCaseResolutionStatus.getTestCaseReference().getFullyQualifiedName(),
|
|
||||||
latestTestCaseResolutionStatus.getStateId().toString());
|
|
||||||
|
|
||||||
// Return the TestCase with the StateId to avoid any unnecessary PATCH when resolving the task
|
// Return the TestCase with the StateId to avoid any unnecessary PATCH when resolving the task
|
||||||
// in the feed repo,
|
// in the feed repo,
|
||||||
// since the `threadContext.getAboutEntity()` will give us the task with the `incidentId`
|
// since the `threadContext.getAboutEntity()` will give us the task with the `incidentId`
|
||||||
|
|||||||
@ -141,7 +141,7 @@ public class TestCaseResolutionStatusRepository
|
|||||||
@Override
|
@Override
|
||||||
@Transaction
|
@Transaction
|
||||||
public TestCaseResolutionStatus createNewRecord(
|
public TestCaseResolutionStatus createNewRecord(
|
||||||
TestCaseResolutionStatus recordEntity, String extension, String recordFQN) {
|
TestCaseResolutionStatus recordEntity, String recordFQN) {
|
||||||
|
|
||||||
TestCaseResolutionStatus lastIncident =
|
TestCaseResolutionStatus lastIncident =
|
||||||
getLatestRecord(recordEntity.getTestCaseReference().getFullyQualifiedName());
|
getLatestRecord(recordEntity.getTestCaseReference().getFullyQualifiedName());
|
||||||
@ -168,19 +168,12 @@ public class TestCaseResolutionStatusRepository
|
|||||||
|
|
||||||
switch (recordEntity.getTestCaseResolutionStatusType()) {
|
switch (recordEntity.getTestCaseResolutionStatusType()) {
|
||||||
case New -> {
|
case New -> {
|
||||||
// If there is already an unresolved incident, return it without doing any
|
// If there is already an existing New incident we'll return it
|
||||||
// further logic.
|
|
||||||
if (Boolean.TRUE.equals(unresolvedIncident(lastIncident))) {
|
if (Boolean.TRUE.equals(unresolvedIncident(lastIncident))) {
|
||||||
return getLatestRecord(lastIncident.getTestCaseReference().getFullyQualifiedName());
|
return lastIncident;
|
||||||
}
|
}
|
||||||
// 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 -> {
|
case Ack, Assigned -> openOrAssignTask(recordEntity);
|
||||||
/* nothing to do for ACK. The Owner already has the task open */
|
|
||||||
}
|
|
||||||
case Assigned -> assignTask(recordEntity, lastIncident);
|
|
||||||
case Resolved -> {
|
case Resolved -> {
|
||||||
// When the incident is Resolved, we will close the Assigned task.
|
// When the incident is Resolved, we will close the Assigned task.
|
||||||
resolveTask(recordEntity, lastIncident);
|
resolveTask(recordEntity, lastIncident);
|
||||||
@ -192,36 +185,38 @@ public class TestCaseResolutionStatusRepository
|
|||||||
default -> throw new IllegalArgumentException(
|
default -> throw new IllegalArgumentException(
|
||||||
String.format("Invalid status %s", recordEntity.getTestCaseResolutionStatusType()));
|
String.format("Invalid status %s", recordEntity.getTestCaseResolutionStatusType()));
|
||||||
}
|
}
|
||||||
return super.createNewRecord(recordEntity, extension, recordFQN);
|
return super.createNewRecord(recordEntity, recordFQN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openNewTask(TestCaseResolutionStatus incidentStatus) {
|
private void openOrAssignTask(TestCaseResolutionStatus incidentStatus) {
|
||||||
|
switch (incidentStatus.getTestCaseResolutionStatusType()) {
|
||||||
TestCase testCase =
|
case Ack -> // If the incident has been acknowledged, the task will be assigned to the user
|
||||||
Entity.getEntity(incidentStatus.getTestCaseReference(), "owner", Include.NON_DELETED);
|
// who acknowledged it
|
||||||
|
createTask(
|
||||||
createTask(incidentStatus, Collections.singletonList(testCase.getOwner()), "New Incident");
|
incidentStatus, Collections.singletonList(incidentStatus.getUpdatedBy()), "New Incident");
|
||||||
}
|
case Assigned -> {
|
||||||
|
// If no existing task is found (New -> Assigned), we'll create a new one,
|
||||||
private void assignTask(
|
// otherwise (Ack -> Assigned) we'll update the existing
|
||||||
TestCaseResolutionStatus newIncidentStatus, TestCaseResolutionStatus lastIncidentStatus) {
|
Thread existingTask = getIncidentTask(incidentStatus);
|
||||||
|
Assigned assigned =
|
||||||
if (lastIncidentStatus == null) {
|
JsonUtils.convertValue(
|
||||||
throw new IncidentManagerException(
|
incidentStatus.getTestCaseResolutionStatusDetails(), Assigned.class);
|
||||||
|
if (existingTask == null) {
|
||||||
|
// New -> Assigned flow
|
||||||
|
createTask(
|
||||||
|
incidentStatus, Collections.singletonList(assigned.getAssignee()), "New Incident");
|
||||||
|
} else {
|
||||||
|
// Ack -> Assigned or Assigned -> Assigned flow
|
||||||
|
patchTaskAssignee(
|
||||||
|
existingTask, assigned.getAssignee(), incidentStatus.getUpdatedBy().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Should not land in the default case as we only call this method for Ack and Assigned
|
||||||
|
default -> throw new IllegalArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
"Cannot find the last incident status for stateId %s",
|
"Task cannot be opened for status `%s`",
|
||||||
newIncidentStatus.getStateId()));
|
incidentStatus.getTestCaseResolutionStatusType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread thread = getIncidentTask(lastIncidentStatus);
|
|
||||||
|
|
||||||
Assigned assigned =
|
|
||||||
JsonUtils.convertValue(
|
|
||||||
newIncidentStatus.getTestCaseResolutionStatusDetails(), Assigned.class);
|
|
||||||
User updatedBy =
|
|
||||||
Entity.getEntity(Entity.USER, newIncidentStatus.getUpdatedBy().getId(), "", Include.ALL);
|
|
||||||
|
|
||||||
patchTaskAssignee(thread, assigned.getAssignee(), updatedBy.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resolveTask(
|
private void resolveTask(
|
||||||
@ -234,8 +229,6 @@ public class TestCaseResolutionStatusRepository
|
|||||||
newIncidentStatus.getStateId()));
|
newIncidentStatus.getStateId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread thread = getIncidentTask(lastIncidentStatus);
|
|
||||||
|
|
||||||
Resolved resolved =
|
Resolved resolved =
|
||||||
JsonUtils.convertValue(
|
JsonUtils.convertValue(
|
||||||
newIncidentStatus.getTestCaseResolutionStatusDetails(), Resolved.class);
|
newIncidentStatus.getTestCaseResolutionStatusDetails(), Resolved.class);
|
||||||
@ -249,11 +242,21 @@ public class TestCaseResolutionStatusRepository
|
|||||||
.withTestCaseFQN(testCase.getFullyQualifiedName())
|
.withTestCaseFQN(testCase.getFullyQualifiedName())
|
||||||
.withTestCaseFailureReason(resolved.getTestCaseFailureReason())
|
.withTestCaseFailureReason(resolved.getTestCaseFailureReason())
|
||||||
.withNewValue(resolved.getTestCaseFailureComment());
|
.withNewValue(resolved.getTestCaseFailureComment());
|
||||||
Entity.getFeedRepository()
|
|
||||||
.resolveTask(
|
Thread thread = getIncidentTask(lastIncidentStatus);
|
||||||
new FeedRepository.ThreadContext(thread),
|
|
||||||
updatedBy.getFullyQualifiedName(),
|
if (thread != null) {
|
||||||
resolveTask);
|
// If there is an existing task, we'll resolve it and create a new incident
|
||||||
|
// status with the Resolved status flow
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTask(
|
private void createTask(
|
||||||
|
|||||||
@ -31,4 +31,9 @@ public abstract class EntityTimeSeriesResource<
|
|||||||
entity = repository.createNewRecord(entity, extension, recordFQN);
|
entity = repository.createNewRecord(entity, extension, recordFQN);
|
||||||
return Response.ok(entity).build();
|
return Response.ok(entity).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Response create(T entity, String recordFQN) {
|
||||||
|
entity = repository.createNewRecord(entity, recordFQN);
|
||||||
|
return Response.ok(entity).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -238,7 +238,7 @@ public class TestCaseResolutionStatusResource
|
|||||||
createTestCaseResolutionStatus,
|
createTestCaseResolutionStatus,
|
||||||
securityContext.getUserPrincipal().getName());
|
securityContext.getUserPrincipal().getName());
|
||||||
|
|
||||||
return create(testCaseResolutionStatus, null, testCaseEntity.getFullyQualifiedName());
|
return create(testCaseResolutionStatus, testCaseEntity.getFullyQualifiedName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PATCH
|
@PATCH
|
||||||
|
|||||||
@ -1115,7 +1115,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
|
|||||||
|
|
||||||
// We can get it via API with a list of ongoing incidents
|
// We can get it via API with a list of ongoing incidents
|
||||||
TestCase result = getTestCase(testCaseEntity.getFullyQualifiedName(), ADMIN_AUTH_HEADERS);
|
TestCase result = getTestCase(testCaseEntity.getFullyQualifiedName(), ADMIN_AUTH_HEADERS);
|
||||||
|
UUID incidentId = result.getIncidentId();
|
||||||
assertNotNull(result.getIncidentId());
|
assertNotNull(result.getIncidentId());
|
||||||
|
|
||||||
// Resolving the status triggers resolving the task, which triggers removing the ongoing
|
// Resolving the status triggers resolving the task, which triggers removing the ongoing
|
||||||
@ -1131,10 +1131,22 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
|
|||||||
.withResolvedBy(USER1_REF));
|
.withResolvedBy(USER1_REF));
|
||||||
createTestCaseFailureStatus(createResolvedStatus);
|
createTestCaseFailureStatus(createResolvedStatus);
|
||||||
|
|
||||||
// If we read again, the incident list will be empty
|
|
||||||
result = getTestCase(testCaseEntity.getFullyQualifiedName(), ADMIN_AUTH_HEADERS);
|
result = getTestCase(testCaseEntity.getFullyQualifiedName(), ADMIN_AUTH_HEADERS);
|
||||||
|
assertNotNull(result.getIncidentId());
|
||||||
|
assertEquals(incidentId, result.getIncidentId());
|
||||||
|
|
||||||
assertNull(result.getIncidentId());
|
// Add a new failed result, which will create a NEW incident and start a new stateId
|
||||||
|
putTestCaseResult(
|
||||||
|
testCaseEntity.getFullyQualifiedName(),
|
||||||
|
new TestCaseResult()
|
||||||
|
.withResult("result")
|
||||||
|
.withTestCaseStatus(TestCaseStatus.Failed)
|
||||||
|
.withTimestamp(TestUtils.dateToTimestamp("2024-01-02")),
|
||||||
|
ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
result = getTestCase(testCaseEntity.getFullyQualifiedName(), ADMIN_AUTH_HEADERS);
|
||||||
|
assertNotNull(result.getIncidentId());
|
||||||
|
assertNotEquals(incidentId, result.getIncidentId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user