diff --git a/packit_service/models.py b/packit_service/models.py index 26cdfb02e..6daa7641d 100644 --- a/packit_service/models.py +++ b/packit_service/models.py @@ -5158,6 +5158,19 @@ def get_by_id(cls, group_id: int) -> Optional["LogDetectiveRunGroupModel"]: with sa_session_transaction() as session: return session.query(LogDetectiveRunGroupModel).filter_by(id=group_id).first() + @classmethod + def get_range( + cls, + first: int, + last: int, + ) -> Iterable["LogDetectiveRunGroupModel"]: + with sa_session_transaction() as session: + query = session.query(LogDetectiveRunGroupModel).order_by( + desc(LogDetectiveRunGroupModel.id), + ) + + return query.slice(first, last) + @classmethod def get_running( cls, diff --git a/packit_service/service/api/logdetective.py b/packit_service/service/api/logdetective.py index 97e8442a9..5143ce56c 100644 --- a/packit_service/service/api/logdetective.py +++ b/packit_service/service/api/logdetective.py @@ -120,3 +120,33 @@ def get(self): ) resp.headers["Content-Range"] = f"log-detective-results {first + 1}-{last}/*" return resp + + +@ns.route("/groups") +class LogDetectiveGroupList(Resource): + @ns.expect(pagination_arguments) + @ns.response(HTTPStatus.PARTIAL_CONTENT.value, "Log Detective group list follows") + def get(self): + """List all Log Detective run groups.""" + first, last = indices() + result = [] + for group_model in LogDetectiveRunGroupModel.get_range(first, last): + targets = sorted(group_model.grouped_targets, key=lambda target: target.id) + group_dict = { + "packit_id": group_model.id, + "submitted_time": optional_timestamp(group_model.submitted_time), + "run_ids": sorted(run.id for run in group_model.runs) if group_model.runs else [], + "log_detective_targets": [ + { + "id": target.id, + "target_arch": target.target, + "status": target.status.value, + } + for target in targets + ], + } + group_dict.update(get_project_info_from_build(group_model)) + result.append(group_dict) + resp = response_maker(result, status=HTTPStatus.PARTIAL_CONTENT) + resp.headers["Content-Range"] = f"log-detective-groups {first + 1}-{last}/*" + return resp diff --git a/tests_openshift/conftest.py b/tests_openshift/conftest.py index 8fa41c421..e1ace065a 100644 --- a/tests_openshift/conftest.py +++ b/tests_openshift/conftest.py @@ -2707,5 +2707,13 @@ def a_log_detective_group(branch_project_event_model): log_detective_run_group=group, log_detective_response=SampleValues.ld_log_detective_response, ) + LogDetectiveRunModel.create( + status=LogDetectiveResult.error, + target_build="error-build", + target="fedora-42-aarch64", + build_system=LogDetectiveBuildSystem.koji, + log_detective_analysis_id="error-analysis-id", + log_detective_run_group=group, + ) yield group diff --git a/tests_openshift/service/test_api.py b/tests_openshift/service/test_api.py index 75bdca673..82b17b833 100644 --- a/tests_openshift/service/test_api.py +++ b/tests_openshift/service/test_api.py @@ -1091,7 +1091,34 @@ def test_log_detective_group(client, clean_before_and_after, a_log_detective_gro assert isinstance(response_dict["submitted_time"], int) assert datetime.datetime.fromtimestamp(response_dict["submitted_time"]) assert len(response_dict["run_ids"]) == 1 - assert len(response_dict["log_detective_target_ids"]) == 1 + assert response_dict["log_detective_target_ids"] == sorted( + target.id for target in a_log_detective_group.grouped_targets + ) + assert "log_detective_targets" not in response_dict + + +def test_log_detective_groups_list(client, clean_before_and_after, a_log_detective_group): + response = client.get( + url_for("api.log-detective_log_detective_group_list") + "?page=1&per_page=2", + ) + + assert response.status_code == 206 + assert response.headers["Content-Range"] == "log-detective-groups 1-2/*" + + response_list = response.json + assert len(response_list) == 1 + assert response_list[0]["packit_id"] == a_log_detective_group.id + expected_targets = [ + { + "id": target.id, + "target_arch": target.target, + "status": target.status.value, + } + for target in sorted(a_log_detective_group.grouped_targets, key=lambda target: target.id) + ] + assert response_list[0]["log_detective_targets"] == expected_targets + assert "log_detective_target_ids" not in response_list[0] + assert "log_detective_target_arches" not in response_list[0] def test_log_detective_list(client, clean_before_and_after, a_log_detective_result):