Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: IssueDetails test and build info #711

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions backend/kernelCI_app/typeModels/issueDetails.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel


class IssueDetailsPathParameters(BaseModel):
issue_id: str
version: int
6 changes: 6 additions & 0 deletions backend/kernelCI_app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,10 @@ def viewCache(view):
path("issue/<str:issue_id>/version/<str:version>",
viewCache(views.IssueDetails),
name="issueDetails"),
path("issue/<str:issue_id>/version/<str:version>/tests",
viewCache(views.IssueDetailsTests),
name="issueDetailsTests"),
path("issue/<str:issue_id>/version/<str:version>/builds",
viewCache(views.IssueDetailsBuilds),
name="issueDetailsBuilds"),
]
59 changes: 59 additions & 0 deletions backend/kernelCI_app/views/issueDetailsBuildsView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from http import HTTPStatus
from typing import Dict, Optional
from django.http import JsonResponse
from django.views import View
from kernelCI_app.helpers.errorHandling import create_error_response
from kernelCI_app.models import Incidents
from kernelCI_app.typeModels.issueDetails import IssueDetailsPathParameters
from pydantic import ValidationError


class IssueDetailsBuilds(View):
def _fetch_incidents(self, *, issue_id: str, version: int) -> Optional[Dict]:
fields = [
"build__id",
"build__architecture",
"build__config_name",
"build__valid",
"build__start_time",
"build__duration",
"build__compiler",
]

builds = Incidents.objects.filter(
issue_id=issue_id, issue_version=version
).values(*fields)

return [
{
"id": build["build__id"],
"architecture": build["build__architecture"],
"config_name": build["build__config_name"],
"valid": build["build__valid"],
"start_time": build["build__start_time"],
"duration": build["build__duration"],
"compiler": build["build__compiler"],
}
for build in builds
]

def get(
self, _request, issue_id: Optional[str], version: Optional[str]
) -> JsonResponse:
try:
parsed_params = IssueDetailsPathParameters(
issue_id=issue_id, version=version
)
except ValidationError as e:
return create_error_response(e.json())

builds_data = self._fetch_incidents(
issue_id=parsed_params.issue_id, version=parsed_params.version
)

if builds_data is None:
return create_error_response(
error_message="Builds not found", status_code=HTTPStatus.NOT_FOUND
)

return JsonResponse(builds_data, safe=False)
57 changes: 57 additions & 0 deletions backend/kernelCI_app/views/issueDetailsTestsView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from http import HTTPStatus
from typing import Dict, Optional
from django.http import JsonResponse
from django.views import View
from kernelCI_app.helpers.errorHandling import create_error_response
from kernelCI_app.models import Incidents
from kernelCI_app.typeModels.issueDetails import IssueDetailsPathParameters
from pydantic import ValidationError


class IssueDetailsTests(View):
def _fetch_incidents(self, *, issue_id: str, version: int) -> Optional[Dict]:
fields = [
"test__id",
"test__duration",
"test__status",
"test__path",
"test__start_time",
"test__environment_compatible",
]

tests = Incidents.objects.filter(
issue_id=issue_id, issue_version=version
).values(*fields)

return [
{
"id": test["test__id"],
"duration": test["test__duration"],
"status": test["test__status"],
"path": test["test__path"],
"start_time": test["test__start_time"],
"environment_compatible": test["test__environment_compatible"],
}
for test in tests
]

def get(
self, _request, issue_id: Optional[str], version: Optional[str]
) -> JsonResponse:
try:
parsed_params = IssueDetailsPathParameters(
issue_id=issue_id, version=version
)
except ValidationError as e:
return create_error_response(e.json())

tests_data = self._fetch_incidents(
issue_id=parsed_params.issue_id, version=parsed_params.version
)

if tests_data is None:
return create_error_response(
error_message="Tests not found", status_code=HTTPStatus.NOT_FOUND
)

return JsonResponse(tests_data, safe=False)
35 changes: 16 additions & 19 deletions backend/kernelCI_app/views/issueDetailsView.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from http import HTTPStatus
from typing import Dict, Optional
from django.http import HttpResponseBadRequest, HttpResponseNotFound, JsonResponse
from django.http import JsonResponse
from django.views import View
from kernelCI_app.helpers.errorHandling import create_error_response
from kernelCI_app.models import Issues
from kernelCI_app.utils import getErrorResponseBody
from kernelCI_app.typeModels.issueDetails import IssueDetailsPathParameters
from pydantic import ValidationError


class IssueDetails(View):
Expand Down Expand Up @@ -31,27 +34,21 @@ def _fetch_issue(self, *, issue_id: str, version: int) -> Optional[Dict]:

return query

def get(self, _request, issue_id: Optional[str], version: Optional[str]):
missing_params = []
if issue_id is None:
missing_params.append("issue_id")
if version is None:
missing_params.append("version")
if len(missing_params) != 0:
return HttpResponseBadRequest(
getErrorResponseBody("Missing parameters: ", missing_params)
)

def get(self, _request, issue_id: Optional[str], version: Optional[str]) -> JsonResponse:
try:
parsed_version = int(version)
except ValueError:
return HttpResponseBadRequest(
getErrorResponseBody("Invalid version parameter, must be an integer")
parsed_params = IssueDetailsPathParameters(
issue_id=issue_id, version=version
)
except ValidationError as e:
return create_error_response(e.json())

issue_data = self._fetch_issue(issue_id=issue_id, version=parsed_version)
issue_data = self._fetch_issue(
issue_id=parsed_params.issue_id, version=parsed_params.version
)

if issue_data is None:
return HttpResponseNotFound(getErrorResponseBody("Issue not found"))
return create_error_response(
error_message="Issue not found", status_code=HTTPStatus.NOT_FOUND
)

return JsonResponse(issue_data)
53 changes: 53 additions & 0 deletions backend/requests/issue-details-builds-get.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
http http://localhost:8000/api/issue/maestro:f61865c29254c199ba015e5f48acfc070aff0eb0/version/0/builds

# HTTP/1.1 200 OK
# Cache-Control: max-age=0
# Content-Length: 872
# Content-Type: application/json
# Cross-Origin-Opener-Policy: same-origin
# Date: Mon, 23 Dec 2024 19:25:24 GMT
# Expires: Mon, 23 Dec 2024 19:25:24 GMT
# Referrer-Policy: same-origin
# Server: WSGIServer/0.2 CPython/3.12.7
# Vary: origin
# X-Content-Type-Options: nosniff
# X-Frame-Options: DENY

# [
# {
# "architecture": "arm",
# "compiler": "clang-17",
# "config_name": "imx_v6_v7_defconfig+allmodconfig",
# "duration": null,
# "id": "maestro:673fcc50923416c0c98dfea6",
# "start_time": "2024-11-22T00:12:00.124Z",
# "valid": false
# },
# {
# "architecture": "arm",
# "compiler": "clang-17",
# "config_name": "imx_v6_v7_defconfig+allmodconfig",
# "duration": null,
# "id": "maestro:67406b8c923416c0c98f75ad",
# "start_time": "2024-11-22T11:31:24.629Z",
# "valid": false
# },
# {
# "architecture": "arm",
# "compiler": "clang-17",
# "config_name": "imx_v6_v7_defconfig+allmodconfig",
# "duration": null,
# "id": "maestro:67409557923416c0c98fa534",
# "start_time": "2024-11-22T14:29:43.011Z",
# "valid": false
# },
# {
# "architecture": "arm",
# "compiler": "clang-17",
# "config_name": "imx_v6_v7_defconfig+allmodconfig",
# "duration": null,
# "id": "maestro:674079b4923416c0c98f79ba",
# "start_time": "2024-11-22T12:31:48.309Z",
# "valid": false
# }
# ]
65 changes: 65 additions & 0 deletions backend/requests/issue-details-tests-get.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
http 'http://localhost:8000/api/issue/maestro:0820fe153b255bf52750bbf1fecb198d8772f5a9/version/0/tests'

# HTTP/1.1 200 OK
# Cache-Control: max-age=0
# Content-Length: 904
# Content-Type: application/json
# Cross-Origin-Opener-Policy: same-origin
# Date: Mon, 23 Dec 2024 16:38:59 GMT
# Expires: Mon, 23 Dec 2024 16:38:59 GMT
# Referrer-Policy: same-origin
# Server: WSGIServer/0.2 CPython/3.12.7
# Vary: origin
# X-Content-Type-Options: nosniff
# X-Frame-Options: DENY

# [
# {
# "duration": null,
# "environment_compatible": [
# "google,tomato-rev2",
# "google,tomato",
# "mediatek,mt8195"
# ],
# "id": "maestro:674ef566063ce49a02bf7b6d",
# "path": "boot.nfs",
# "start_time": "2024-12-03T12:11:18.636Z",
# "status": "FAIL"
# },
# {
# "duration": null,
# "environment_compatible": [
# "google,tomato-rev2",
# "google,tomato",
# "mediatek,mt8195"
# ],
# "id": "maestro:674fddf9089e99b3f2b506f7",
# "path": "boot.nfs",
# "start_time": "2024-12-04T04:43:37.701Z",
# "status": "FAIL"
# },
# {
# "duration": null,
# "environment_compatible": [
# "google,tomato-rev2",
# "google,tomato",
# "mediatek,mt8195"
# ],
# "id": "maestro:674fd928089e99b3f2b4b523",
# "path": "boot",
# "start_time": "2024-12-04T04:23:04.822Z",
# "status": "FAIL"
# },
# {
# "duration": null,
# "environment_compatible": [
# "google,tomato-rev2",
# "google,tomato",
# "mediatek,mt8195"
# ],
# "id": "maestro:6750002f089e99b3f2b7e106",
# "path": "boot",
# "start_time": "2024-12-04T07:09:35.087Z",
# "status": "FAIL"
# }
# ]
44 changes: 44 additions & 0 deletions dashboard/src/api/issueDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useQuery } from '@tanstack/react-query';

import type { TIssueDetails } from '@/types/issueDetails';

import type { BuildsTableBuild, TestHistory } from '@/types/general';

import http from './api';

const fetchIssueDetailsData = async (
Expand All @@ -25,3 +27,45 @@ export const useIssueDetails = (
queryFn: () => fetchIssueDetailsData(issueId, versionNumber),
});
};

const fetchIssueDetailsTests = async (
issueId: string,
versionNumber: string,
): Promise<TestHistory[]> => {
const res = await http.get(
`/api/issue/${issueId}/version/${versionNumber}/tests`,
);

return res.data;
};

export const useIssueDetailsTests = (
issueId: string,
versionNumber: string,
): UseQueryResult<TestHistory[]> => {
return useQuery({
queryKey: ['issueTestsData', issueId, versionNumber],
queryFn: () => fetchIssueDetailsTests(issueId, versionNumber),
});
};

const fetchIssueDetailsBuilds = async (
issueId: string,
versionNumber: string,
): Promise<BuildsTableBuild[]> => {
const res = await http.get(
`/api/issue/${issueId}/version/${versionNumber}/builds`,
);

return res.data;
};

export const useIssueDetailsBuilds = (
issueId: string,
versionNumber: string,
): UseQueryResult<BuildsTableBuild[]> => {
return useQuery({
queryKey: ['issueBuildsData', issueId, versionNumber],
queryFn: () => fetchIssueDetailsBuilds(issueId, versionNumber),
});
};
Loading
Loading