diff --git a/backend/kernelCI_app/typeModels/buildDetails.py b/backend/kernelCI_app/typeModels/buildDetails.py index 974a511d..ead9382c 100644 --- a/backend/kernelCI_app/typeModels/buildDetails.py +++ b/backend/kernelCI_app/typeModels/buildDetails.py @@ -1,3 +1,6 @@ +from typing import List +from pydantic import BaseModel, RootModel + from kernelCI_app.typeModels.commonDetails import BuildHistoryItem from kernelCI_app.typeModels.databases import ( Origin, @@ -12,6 +15,12 @@ Build__LogExcerpt, Build__InputFiles, Build__OutputFiles, + Test__Id, + Test__Duration, + Test__Path, + Test__Status, + Test__StartTime, + Test__EnvironmentCompatible, ) @@ -28,3 +37,16 @@ class BuildDetailsResponse(BuildHistoryItem): log_excerpt: Build__LogExcerpt input_files: Build__InputFiles output_files: Build__OutputFiles + + +class BuildTestItem(BaseModel): + id: Test__Id + status: Test__Status + duration: Test__Duration + path: Test__Path + start_time: Test__StartTime + environment_compatible: Test__EnvironmentCompatible + + +class BuildTestsResponse(RootModel): + root: List[BuildTestItem] diff --git a/backend/kernelCI_app/typeModels/databases.py b/backend/kernelCI_app/typeModels/databases.py index 63717e9c..1bb38023 100644 --- a/backend/kernelCI_app/typeModels/databases.py +++ b/backend/kernelCI_app/typeModels/databases.py @@ -11,9 +11,9 @@ failure_status_list = [ERROR_STATUS, FAIL_STATUS, MISS_STATUS] # "NULL" must be added manually because the database return None -type StatusValues = Literal["FAIL", "PASS", "SKIP", "ERROR", "MISS", "NULL"] +type StatusValues = Literal["FAIL", "PASS", "SKIP", "ERROR", "MISS", "NULL", "DONE"] -type DatabaseStatusValues = Literal["FAIL", "PASS", "SKIP", "ERROR", "MISS"] +type DatabaseStatusValues = Literal["FAIL", "PASS", "SKIP", "ERROR", "MISS", "DONE"] type Origin = str @@ -39,6 +39,13 @@ type Build__InputFiles = Optional[Union[List[Dict[str, Any]], Dict[str, Any]]] type Build__OutputFiles = Optional[Union[List[Dict[str, Any]], Dict[str, Any]]] +type Test__Id = str +type Test__Status = Optional[DatabaseStatusValues] +type Test__Duration = Optional[float] +type Test__Path = Optional[str] +type Test__StartTime = Optional[datetime] +type Test__EnvironmentCompatible = Optional[List[str]] + class Issues(BaseModel): field_timestamp: Optional[datetime] = Field(None, alias="_timestamp") diff --git a/backend/kernelCI_app/views/buildTestsView.py b/backend/kernelCI_app/views/buildTestsView.py index c26fa937..354fd271 100644 --- a/backend/kernelCI_app/views/buildTestsView.py +++ b/backend/kernelCI_app/views/buildTestsView.py @@ -1,20 +1,31 @@ -from django.http import JsonResponse -from django.views import View from http import HTTPStatus -from kernelCI_app.helpers.errorHandling import create_error_response +from kernelCI_app.typeModels.buildDetails import BuildTestsResponse from kernelCI_app.models import Tests +from drf_spectacular.utils import extend_schema +from rest_framework.views import APIView +from rest_framework.response import Response +from pydantic import ValidationError -class BuildTests(View): - def get(self, request, build_id): +class BuildTests(APIView): + @extend_schema(responses=BuildTestsResponse) + def get(self, request, build_id: str) -> Response: result = Tests.objects.filter(build_id=build_id).values( "id", "duration", "status", "path", "start_time", "environment_compatible" ) if not result: - return create_error_response( - error_message="No tests found for this build", - status_code=HTTPStatus.OK, + return Response( + data={"error": "No tests found for this build"}, + status=HTTPStatus.OK, ) - return JsonResponse(list(result), safe=False) + try: + valid_response = BuildTestsResponse(result) + except ValidationError as e: + return Response( + data={"error": e.errors()}, + status=HTTPStatus.INTERNAL_SERVER_ERROR, + ) + + return Response(valid_response.model_dump()) diff --git a/backend/schema.yml b/backend/schema.yml index ddb5ef1c..716d022b 100644 --- a/backend/schema.yml +++ b/backend/schema.yml @@ -26,6 +26,28 @@ paths: schema: $ref: '#/components/schemas/BuildDetailsResponse' description: '' + /api/build/{build_id}/tests: + get: + operationId: build_tests_retrieve + parameters: + - in: path + name: build_id + schema: + type: string + required: true + tags: + - build + security: + - cookieAuth: [] + - basicAuth: [] + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/BuildTestsResponse' + description: '' /api/hardware/{hardware_id}: post: operationId: hardware_create @@ -832,6 +854,34 @@ components: - unknown_issues title: BuildSummary type: object + BuildTestItem: + properties: + id: + $ref: '#/components/schemas/Test__Id' + status: + $ref: '#/components/schemas/Test__Status' + duration: + $ref: '#/components/schemas/Test__Duration' + path: + $ref: '#/components/schemas/Test__Path' + start_time: + $ref: '#/components/schemas/Test__StartTime' + environment_compatible: + $ref: '#/components/schemas/Test__EnvironmentCompatible' + required: + - id + - status + - duration + - path + - start_time + - environment_compatible + title: BuildTestItem + type: object + BuildTestsResponse: + items: + $ref: '#/components/schemas/BuildTestItem' + title: BuildTestsResponse + type: array Build__Architecture: anyOf: - type: string @@ -931,6 +981,15 @@ components: - tests title: CommonDetailsTestsResponse type: object + DatabaseStatusValues: + enum: + - FAIL + - PASS + - SKIP + - ERROR + - MISS + - DONE + type: string DetailsFilters: properties: all: @@ -1521,6 +1580,31 @@ components: - failed_platforms title: TestSummary type: object + Test__Duration: + anyOf: + - type: number + - type: 'null' + Test__EnvironmentCompatible: + anyOf: + - items: + type: string + type: array + - type: 'null' + Test__Id: + type: string + Test__Path: + anyOf: + - type: string + - type: 'null' + Test__StartTime: + anyOf: + - format: date-time + type: string + - type: 'null' + Test__Status: + anyOf: + - $ref: '#/components/schemas/DatabaseStatusValues' + - type: 'null' Tree: properties: index: