Skip to content

Commit 12013b2

Browse files
johnaoharalampajr
andcommitted
Add new test service api to return label values for filter: Fixes #1767
Apply suggestions from code review Co-authored-by: Andrea Lamparelli <[email protected]>
1 parent 375aa52 commit 12013b2

File tree

6 files changed

+129
-13
lines changed

6 files changed

+129
-13
lines changed

docs/site/content/en/openapi/openapi.yaml

+23
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,29 @@ paths:
20322032
application/json:
20332033
schema:
20342034
$ref: '#/components/schemas/TestExport'
2035+
/api/test/{id}/filteringLabelValues:
2036+
get:
2037+
tags:
2038+
- Test
2039+
description: List all unique Label Values for a Test
2040+
operationId: filteringLabelValues
2041+
parameters:
2042+
- name: id
2043+
in: path
2044+
description: Test ID to retrieve Filtering Label Values for
2045+
required: true
2046+
schema:
2047+
format: int32
2048+
type: integer
2049+
example: 10
2050+
responses:
2051+
"200":
2052+
description: OK
2053+
content:
2054+
application/json:
2055+
schema:
2056+
type: array
2057+
items: {}
20352058
/api/test/{id}/fingerprint:
20362059
get:
20372060
tags:

horreum-api/src/main/java/io/hyperfoil/tools/horreum/api/services/TestService.java

+15
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,21 @@ List<ExportedLabelValues> labelValues(
252252
@QueryParam("exclude") @Separator(",") List<String> exclude,
253253
@QueryParam("multiFilter") @DefaultValue("false") boolean multiFilter);
254254

255+
@GET
256+
@Path("{id}/filteringLabelValues")
257+
@Operation(description="List all unique Label Values for a Test")
258+
@Parameters(value = {
259+
@Parameter(name = "id", description = "Test ID to retrieve Filtering Label Values for", example = "10"),
260+
})
261+
@APIResponses(
262+
value = { @APIResponse( responseCode = "200",
263+
content = {
264+
@Content ( schema = @Schema(type = SchemaType.ARRAY, implementation = Object.class)) }
265+
)}
266+
)
267+
List<ObjectNode> filteringLabelValues(
268+
@PathParam("id") int testId);
269+
255270
@POST
256271
@Consumes(MediaType.APPLICATION_JSON)
257272
@Path("{id}/transformers")

horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/TestServiceImpl.java

+30
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import jakarta.transaction.TransactionManager;
4747
import jakarta.transaction.Transactional;
4848
import jakarta.ws.rs.DefaultValue;
49+
import jakarta.ws.rs.PathParam;
4950
import jakarta.ws.rs.WebApplicationException;
5051
import jakarta.ws.rs.core.Response;
5152
import org.hibernate.Hibernate;
@@ -103,6 +104,15 @@ SELECT DISTINCT COALESCE(jsonb_object_agg(label.name, lv.value) FILTER (WHERE la
103104
) select * from combined FILTER_PLACEHOLDER ORDER_PLACEHOLDER limit :limit offset :offset
104105
""";
105106

107+
protected static final String LABEL_VALUES_SUMMARY_QUERY = """
108+
SELECT DISTINCT COALESCE(jsonb_object_agg(label.name, lv.value), '{}'::jsonb) AS values
109+
FROM dataset
110+
INNER JOIN label_values lv ON dataset.id = lv.dataset_id
111+
INNER JOIN label ON label.id = lv.label_id
112+
WHERE dataset.testid = :testId AND label.filtering
113+
GROUP BY dataset.id, runId
114+
""";
115+
106116

107117
//@formatter:on
108118
@Inject
@@ -821,6 +831,26 @@ public List<ExportedLabelValues> labelValues(int testId, String filter, String b
821831
return ExportedLabelValues.parse( query.getResultList() );
822832
}
823833

834+
@Transactional
835+
@WithRoles
836+
@Override
837+
public List<ObjectNode> filteringLabelValues(
838+
@PathParam("id") int testId){
839+
840+
TestDAO.findByIdOptional(testId).orElseThrow(() -> ServiceException.serverError("Cannot find test " + testId));
841+
842+
NativeQuery<ObjectNode> query = ((NativeQuery<ObjectNode>) em.createNativeQuery(LABEL_VALUES_SUMMARY_QUERY))
843+
.setParameter("testId", testId);
844+
845+
query
846+
.unwrap(NativeQuery.class)
847+
.addScalar("values", JsonBinaryType.INSTANCE);
848+
List<ObjectNode> filters = query.getResultList();
849+
850+
return filters != null ? filters : new ArrayList<>();
851+
852+
}
853+
824854
@WithRoles
825855
@Transactional
826856
@Override

horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/BaseServiceTest.java

+9
Original file line numberDiff line numberDiff line change
@@ -503,11 +503,20 @@ protected int addLabel(Schema schema, String name, String function, Extractor...
503503
return lastAddedLabelId;
504504
}
505505

506+
protected int addLabel(Schema schema, String name, String function, boolean filtering, boolean metric, Extractor... extractors) {
507+
lastAddedLabelId = postLabel(schema, name, function, null, filtering, metric, extractors);
508+
return lastAddedLabelId;
509+
}
510+
506511
protected int updateLabel(Schema schema, int labelId, String name, String function, Extractor... extractors) {
507512
return postLabel(schema, name, function, l -> l.id = labelId, extractors);
508513
}
509514

510515
protected int postLabel(Schema schema, String name, String function, Consumer<Label> mutate, Extractor... extractors) {
516+
return postLabel(schema, name, function, mutate, true, true, extractors);
517+
}
518+
519+
protected int postLabel(Schema schema, String name, String function, Consumer<Label> mutate, boolean filtering, boolean metric, Extractor... extractors) {
511520
Label l = new Label();
512521
l.name = name;
513522
l.function = function;

horreum-backend/src/test/java/io/hyperfoil/tools/horreum/svc/TestServiceTest.java

+50
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,56 @@ public void testLabelValues(TestInfo info) throws InterruptedException {
248248
assertEquals(2, values.size());
249249
assertTrue(values.get(1).values.containsKey("value"));
250250
}
251+
@org.junit.jupiter.api.Test
252+
public void testFilterLabelValues(TestInfo info) throws InterruptedException {
253+
Test test = createTest(createExampleTest(getTestName(info)));
254+
255+
String name = info.getTestClass().map(Class::getName).orElse("<unknown>") + "." + info.getDisplayName();
256+
Schema schema = createSchema(name, uriForTest(info, "1.0"));
257+
258+
259+
addLabel(schema, "filter-1", null, true, false, new Extractor("filter", "$.filter1", false));
260+
addLabel(schema, "filter-2", null, true, false, new Extractor("filter", "$.filter2", false));
261+
262+
263+
BlockingQueue<Dataset.LabelsUpdatedEvent> newDatasetQueue = serviceMediator.getEventQueue(AsyncEventChannels.DATASET_UPDATED_LABELS, test.id);
264+
ObjectNode run;
265+
266+
run = runWithValue(42, schema);
267+
run.put("filter1", "foo");
268+
run.put("filter2", "bar");
269+
uploadRun(run, test.name);
270+
271+
run = runWithValue(43, schema);
272+
run.put("filter1", "foo");
273+
run.put("filter2", "bar");
274+
uploadRun(run, test.name);
275+
276+
run = runWithValue(44, schema);
277+
run.put("filter1", "biz");
278+
run.put("filter2", "bar");
279+
uploadRun(run, test.name);
280+
281+
run = runWithValue(45, schema);
282+
run.put("filter1", "foo");
283+
run.put("filter2", "baz");
284+
uploadRun(run, test.name);
285+
286+
for( int i = 0 ; i < 4; i++){
287+
assertNotNull(newDatasetQueue.poll(10, TimeUnit.SECONDS));
288+
}
289+
290+
List<ObjectNode> values = jsonRequest().get("/api/test/" + test.id + "/filteringLabelValues").then().statusCode(200).
291+
extract().body().as(new TypeRef<>() {});
292+
assertNotNull(values);
293+
assertFalse(values.isEmpty());
294+
assertEquals(3, values.size());
295+
assertNotNull(values.stream().filter( node -> node.get("filter-1").asText().equals("foo") && node.get("filter-2").asText().equals("bar")).findAny().orElse(null));
296+
assertNotNull(values.stream().filter( node -> node.get("filter-1").asText().equals("biz") && node.get("filter-2").asText().equals("bar")).findAny().orElse(null));
297+
assertNotNull(values.stream().filter( node -> node.get("filter-1").asText().equals("foo") && node.get("filter-2").asText().equals("baz")).findAny().orElse(null));
298+
299+
}
300+
251301
@org.junit.jupiter.api.Test
252302
public void testImportFromFile() throws JsonProcessingException {
253303
Path p = new File(getClass().getClassLoader().getResource(".").getPath()).toPath();

horreum-web/src/domain/runs/TestDatasets.tsx

+2-13
Original file line numberDiff line numberDiff line change
@@ -205,20 +205,9 @@ export default function TestDatasets() {
205205
})
206206
return allColumns
207207
}, [test, token, comparedDatasets, viewId, views])
208-
const flattenLabelValues = (labelValues: Array<ExportedLabelValues>) => {
209-
const resultArr : any = [];
210-
labelValues.forEach( (labelValue) => {
211-
resultArr.push(labelValue.values)
212-
})
213-
return resultArr;
214-
}
215208

216-
const labelsSource = useCallback(() => {
217-
return testApi.labelValues(testIdInt)
218-
.then((result: Array<ExportedLabelValues>) => {
219-
return flattenLabelValues(result);
220-
})
221-
}, [testIdInt, teams, token])
209+
const labelsSource = useCallback(() => testApi.filteringLabelValues(testIdInt), [testIdInt, teams, token])
210+
222211
return (
223212
<>
224213
<Toolbar className="pf-v5-u-justify-content-space-between" style={{ width: "100%" }}>

0 commit comments

Comments
 (0)