Skip to content

Commit

Permalink
GEN 1211 - Added TTResp and TTReso metrics (#18033)
Browse files Browse the repository at this point in the history
* fix import issue

* feat: compute test case resolution metrics

* feat: added  index key to resolution index

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
  • Loading branch information
TeddyCr and chirag-madlani authored Sep 30, 2024
1 parent daa0345 commit 9552886
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4374,6 +4374,12 @@ default String getTimeSeriesTableName() {
+ "WHERE stateId = :stateId ORDER BY timestamp DESC")
List<String> listTestCaseResolutionStatusesForStateId(@Bind("stateId") String stateId);

@SqlQuery(
value =
"SELECT json FROM test_case_resolution_status_time_series "
+ "WHERE stateId = :stateId ORDER BY timestamp ASC LIMIT 1")
String listFirstTestCaseResolutionStatusesForStateId(@Bind("stateId") String stateId);

@SqlUpdate(
"DELETE FROM test_case_resolution_status_time_series WHERE entityFQNHash = :entityFQNHash")
void delete(@BindFQN("entityFQNHash") String entityFQNHash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.openmetadata.schema.entity.teams.User;
import org.openmetadata.schema.tests.TestCase;
import org.openmetadata.schema.tests.type.Assigned;
import org.openmetadata.schema.tests.type.Metric;
import org.openmetadata.schema.tests.type.Resolved;
import org.openmetadata.schema.tests.type.Severity;
import org.openmetadata.schema.tests.type.TestCaseResolutionStatus;
Expand All @@ -49,6 +50,8 @@
public class TestCaseResolutionStatusRepository
extends EntityTimeSeriesRepository<TestCaseResolutionStatus> {
public static final String COLLECTION_PATH = "/v1/dataQuality/testCases/testCaseIncidentStatus";
public static final String TIME_TO_RESPONSE = "timeToResponse";
public static final String TIME_TO_RESOLUTION = "timeToResolution";

public TestCaseResolutionStatusRepository() {
super(
Expand All @@ -75,6 +78,21 @@ public ResultList<TestCaseResolutionStatus> listTestCaseResolutionStatusesForSta
return getResultList(testCaseResolutionStatuses, null, null, testCaseResolutionStatuses.size());
}

private TestCaseResolutionStatus listFirstTestCaseResolutionStatusForStateId(UUID stateId) {
String json =
((CollectionDAO.TestCaseResolutionStatusTimeSeriesDAO) timeSeriesDao)
.listFirstTestCaseResolutionStatusesForStateId(stateId.toString());

if (json == null) {
return null;
}

TestCaseResolutionStatus testCaseResolutionStatus =
JsonUtils.readValue(json, TestCaseResolutionStatus.class);
setInheritedFields(testCaseResolutionStatus);
return testCaseResolutionStatus;
}

public RestUtil.PatchResponse<TestCaseResolutionStatus> patch(
UUID id, JsonPatch patch, String user)
throws IntrospectionException, InvocationTargetException, IllegalAccessException {
Expand Down Expand Up @@ -190,6 +208,7 @@ public void storeInternal(
: recordEntity.getSeverity());
}

setResolutionMetrics(lastIncident, recordEntity);
inferIncidentSeverity(recordEntity);

switch (recordEntity.getTestCaseResolutionStatusType()) {
Expand Down Expand Up @@ -438,4 +457,35 @@ protected static UUID getOrCreateIncident(TestCase testCase, String updatedBy) {

return incident.getStateId();
}

private void setResolutionMetrics(
TestCaseResolutionStatus lastIncident, TestCaseResolutionStatus newIncident) {
List<Metric> metrics = new ArrayList<>();
if (lastIncident == null) return;

if (lastIncident.getTestCaseResolutionStatusType().equals(TestCaseResolutionStatusTypes.New)
&& !newIncident
.getTestCaseResolutionStatusType()
.equals(TestCaseResolutionStatusTypes.Resolved)) {
// Time to response is New (1st step in the workflow) -> [Any status but Resolved (Last step
// in the workflow)]
long timeToResponse = newIncident.getTimestamp() - lastIncident.getTimestamp();
Metric metric = new Metric().withName(TIME_TO_RESPONSE).withValue((double) timeToResponse);
metrics.add(metric);
}

if (newIncident
.getTestCaseResolutionStatusType()
.equals(TestCaseResolutionStatusTypes.Resolved)) {
TestCaseResolutionStatus firstIncidentInWorkflow =
listFirstTestCaseResolutionStatusForStateId(newIncident.getStateId());
if (firstIncidentInWorkflow != null) {
long timeToResolution = newIncident.getTimestamp() - firstIncidentInWorkflow.getTimestamp();
Metric metric =
new Metric().withName(TIME_TO_RESOLUTION).withValue((double) timeToResolution);
metrics.add(metric);
}
}
if (!metrics.isEmpty()) newIncident.setMetrics(metrics);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@
}
}
},
"metrics": {
"type": "nested",
"properties": {
"name": {
"type": "keyword",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"value": {
"type": "float"
}
}
},
"updatedBy": {
"properties": {
"id": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,23 @@
}
}
},
"metrics": {
"type": "nested",
"properties": {
"name": {
"type": "keyword",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"value": {
"type": "float"
}
}
},
"updatedBy": {
"properties": {
"id": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,23 @@
}
}
},
"metrics": {
"type": "nested",
"properties": {
"name": {
"type": "keyword",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"value": {
"type": "float"
}
}
},
"updatedBy": {
"properties": {
"id": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package org.openmetadata.service.resources.dqtests;

import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static javax.ws.rs.core.Response.Status.OK;
Expand Down Expand Up @@ -334,7 +333,7 @@ void getTestCaseWithResult(TestInfo test) throws IOException, ParseException {
new CreateTestCaseResult()
.withResult("tested")
.withTestCaseStatus(TestCaseStatus.Success)
.withTimestamp(TestUtils.dateToTimestamp(String.format("2021-09-11")));
.withTimestamp(TestUtils.dateToTimestamp("2021-09-11"));
TestCaseResult testCaseResult =
postTestCaseResult(
testCase.getFullyQualifiedName(), createTestCaseResult, ADMIN_AUTH_HEADERS);
Expand All @@ -359,7 +358,7 @@ void getTestCaseWithResult(TestInfo test) throws IOException, ParseException {
new CreateTestCaseResult()
.withResult("tested")
.withTestCaseStatus(TestCaseStatus.Success)
.withTimestamp(TestUtils.dateToTimestamp(String.format("2021-09-01")));
.withTimestamp(TestUtils.dateToTimestamp("2021-09-01"));
postTestCaseResult(testCase.getFullyQualifiedName(), createTestCaseResult, ADMIN_AUTH_HEADERS);
testCase = getTestCase(testCase.getFullyQualifiedName(), queryParams, ADMIN_AUTH_HEADERS);
assertEquals(testCaseResult, testCase.getTestCaseResult());
Expand All @@ -377,7 +376,7 @@ void getTestCaseWithResult(TestInfo test) throws IOException, ParseException {
new CreateTestCaseResult()
.withResult("tested")
.withTestCaseStatus(TestCaseStatus.Success)
.withTimestamp(TestUtils.dateToTimestamp(String.format("2021-09-21")));
.withTimestamp(TestUtils.dateToTimestamp("2021-09-21"));
TestCaseResult futureTestCaseResult =
postTestCaseResult(
testCase.getFullyQualifiedName(), createTestCaseResult, ADMIN_AUTH_HEADERS);
Expand Down Expand Up @@ -687,7 +686,7 @@ void test_getSimpleListFromSearch(TestInfo testInfo) throws IOException, ParseEx
testSuiteResourceTest.addTestCasesToLogicalTestSuite(
logicalTestSuite, List.of(testCaseForEL.getId()));

Map queryParams = new HashMap<>();
Map<String, String> queryParams = new HashMap<>();
ResultList<TestCase> allEntities =
listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
assertEquals(testCasesNum, allEntities.getData().size());
Expand All @@ -699,20 +698,20 @@ void test_getSimpleListFromSearch(TestInfo testInfo) throws IOException, ParseEx

queryParams.clear();
queryParams.put("entityLink", testCaseForEL.getEntityLink());
queryParams.put("includeAllTests", true);
queryParams.put("includeAllTests", "true");
allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
assertEquals(1, allEntities.getData().size());
org.assertj.core.api.Assertions.assertThat(allEntities.getData().get(0).getEntityLink())
.contains(testCaseForEL.getEntityLink());

queryParams.clear();
queryParams.put("testPlatforms", TestPlatform.DEEQU);
queryParams.put("testPlatforms", TestPlatform.DEEQU.value());
allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
assertEquals(
0, allEntities.getData().size()); // we don't have any test cases with DEEQU platform

queryParams.clear();
queryParams.put("testPlatforms", TestPlatform.OPEN_METADATA);
queryParams.put("testPlatforms", TestPlatform.OPEN_METADATA.value());
allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
assertEquals(
testCasesNum,
Expand Down Expand Up @@ -814,7 +813,7 @@ void test_getSimpleListFromSearch(TestInfo testInfo) throws IOException, ParseEx
// Test return only the specified test suite ID (Executable)
queryParams.clear();
TestSuite testSuite = testSuites.get(tables.get(0).getFullyQualifiedName());
queryParams.put("testSuiteId", testSuite.getId());
queryParams.put("testSuiteId", testSuite.getId().toString());
queryParams.put("fields", "testSuites");
allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
testCases = allEntities.getData();
Expand All @@ -827,7 +826,7 @@ void test_getSimpleListFromSearch(TestInfo testInfo) throws IOException, ParseEx
.anyMatch(ts -> ts.getId().equals(testSuite.getId()))));

// Test return only the specified test suite ID (Logical)
queryParams.put("testSuiteId", logicalTestSuite.getId());
queryParams.put("testSuiteId", logicalTestSuite.getId().toString());
queryParams.put("fields", "testSuites");
allEntities = listEntitiesFromSearch(queryParams, testCasesNum, 0, ADMIN_AUTH_HEADERS);
testCases = allEntities.getData();
Expand Down Expand Up @@ -1169,6 +1168,20 @@ void post_createTestCaseResultFailure(TestInfo test)
getTestCaseFailureStatus(startTs, endTs, null, null);
assertEquals(4, testCaseFailureStatusResultList.getData().size());

List<TestCaseResolutionStatus> ackStatuses =
testCaseFailureStatusResultList.getData().stream()
.filter(
status ->
status
.getTestCaseResolutionStatusType()
.equals(TestCaseResolutionStatusTypes.Ack))
.toList();

ackStatuses.stream()
.flatMap(status -> status.getMetrics().stream())
.filter(metric -> metric.getName().equals("timeToResponse"))
.forEach(metric -> assertNotNull(metric.getValue()));

// check we have only 2 distinct sequence IDs, one for each test case
List<UUID> stateIds =
testCaseFailureStatusResultList.getData().stream()
Expand Down Expand Up @@ -1472,6 +1485,9 @@ void test_testCaseResolutionTaskCloseWorkflowThruFeed(TestInfo test)
mostRecentTestCaseResolutionStatusData.getTestCaseResolutionStatusType());
assertEquals(
assignedIncident.getStateId(), mostRecentTestCaseResolutionStatusData.getStateId());
mostRecentTestCaseResolutionStatusData.getMetrics().stream()
.filter(m -> m.getName().equals("timeToResolution"))
.forEach(m -> assertNotNull(m.getValue()));
}

@Test
Expand Down Expand Up @@ -2523,9 +2539,8 @@ protected void validateListTestCaseResultsFromSearchWithPagination(
assertEquals(offset, 0);
} else {
// Make sure scrolling back based on offset - limit cursor returns the correct result
backwardPage =
listTestCaseResultsFromSearch(
queryParams, limit, (offset - limit), path, ADMIN_AUTH_HEADERS);
listTestCaseResultsFromSearch(
queryParams, limit, (offset - limit), path, ADMIN_AUTH_HEADERS);
assertEntityPagination(allEntities.getData(), forwardPage, limit, offset);
}
offset = offset + limit;
Expand Down Expand Up @@ -2558,13 +2573,6 @@ protected void validateListTestCaseResultsFromSearchWithPagination(
}
}

public void putTestCaseResult(String fqn, TestCaseResult data, Map<String, String> authHeaders)
throws HttpResponseException {
data.setTestCaseFQN(fqn);
WebTarget target = getResource(testCaseResultsCollectionName).path("/" + fqn);
TestUtils.put(target, data, CREATED, authHeaders);
}

public TestCaseResult postTestCaseResult(
String fqn, CreateTestCaseResult data, Map<String, String> authHeaders)
throws HttpResponseException {
Expand Down Expand Up @@ -3082,7 +3090,7 @@ void aggregate_testCaseResults(TestInfo testInfo) throws IOException, ParseExcep
}

@Test
void createTestCaseResults_wrongTs(TestInfo testInfo) throws IOException, HttpResponseException {
void createTestCaseResults_wrongTs(TestInfo testInfo) throws IOException {
CreateTestCase create = createRequest(testInfo);
TestCase testCase = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
CreateTestCaseResult createTestCaseResult =
Expand Down Expand Up @@ -3146,10 +3154,7 @@ void test_listTestCaseFromSearch(TestInfo testInfo) throws HttpResponseException
assertNotEquals(testCaseResultResultList.getData().size(), 0);
testCaseResultResultList
.getData()
.forEach(
testCaseResult -> {
assertEquals(testCaseResult.getTimestamp(), ts);
});
.forEach(testCaseResult -> assertEquals(testCaseResult.getTimestamp(), ts));

queryParams.clear();
queryParams.put("dataQualityDimension", "Completeness");
Expand Down Expand Up @@ -3178,7 +3183,6 @@ void test_listTestCaseFromSearch(TestInfo testInfo) throws HttpResponseException
.getData()
.forEach(
testCaseResult -> {
EntityReference testDefinition = testCaseResult.getTestCase();
TestCase tc = Entity.getEntity(TEST_CASE, testCase.getId(), "", Include.ALL);
assertTrue(tc.getEntityLink().contains("columns"));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@
{"name": "Severity4"},
{"name": "Severity5"}
]
},
"metric": {
"description": "Representation of a metric.",
"javaType": "org.openmetadata.schema.tests.type.Metric",
"properties": {
"name": {
"description": "Name of the metric.",
"type": "string"
},
"value": {
"description": "Value of the metric.",
"type": "number"
}
}
}
},
"properties": {
Expand Down Expand Up @@ -74,6 +88,13 @@
"severity": {
"description": "Severity failure for the test associated with the resolution.",
"$ref": "#/definitions/severities"
},
"metrics": {
"description": "List of metrics associated with the test case resolution.",
"type": "array",
"items": {
"$ref": "#/definitions/metric"
}
}
},
"required": ["testCaseResolutionStatusType"],
Expand Down

0 comments on commit 9552886

Please sign in to comment.