Skip to content

Commit a07b7e7

Browse files
committed
WIP: add new treeListing view
1 parent 3392a05 commit a07b7e7

File tree

5 files changed

+187
-5
lines changed

5 files changed

+187
-5
lines changed

backend/kernelCI_app/helpers/trees.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def sanitize_tree(
5757
"""Sanitizes a checkout that was returned by a 'treelisting-like' query
5858
5959
Returns a Checkout object"""
60+
6061
build_status = StatusCount(
6162
PASS=checkout["pass_builds"],
6263
FAIL=checkout["fail_builds"],
@@ -87,13 +88,23 @@ def sanitize_tree(
8788
"skip": checkout["skip_boots"],
8889
}
8990

90-
if isinstance(checkout.get("git_commit_tags"), str):
91+
# Has to check if it's a string because sqlite doesn't support ArrayFields.
92+
# So if the query came from sqlite, it will be a string.
93+
git_commit_tags = checkout.get("git_commit_tags")
94+
if isinstance(git_commit_tags, str):
9195
try:
9296
checkout["git_commit_tags"] = json.loads(checkout["git_commit_tags"])
9397
if not isinstance(checkout["git_commit_tags"], list):
9498
checkout["git_commit_tags"] = []
9599
except json.JSONDecodeError:
96100
checkout["git_commit_tags"] = []
101+
elif git_commit_tags and isinstance(git_commit_tags, list):
102+
first_tag = git_commit_tags[0]
103+
if isinstance(first_tag, str):
104+
# The git_commit_tags comes as list[str] on a normal query, but `Checkout`
105+
# expects list[list[str]]. This is a workaround, the queries should *always*
106+
# return a simples list[str].
107+
checkout["git_commit_tags"] = [git_commit_tags]
97108

98109
return Checkout(
99110
**checkout,

backend/kernelCI_app/typeModels/treeListing.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from typing import List
21
from kernelCI_app.typeModels.common import StatusCount
32
from kernelCI_app.typeModels.databases import (
43
Checkout__GitCommitHash,
@@ -71,8 +70,8 @@ class CheckoutFast(CommonCheckouts):
7170

7271

7372
class TreeListingResponse(RootModel):
74-
root: List[Checkout]
73+
root: list[Checkout]
7574

7675

7776
class TreeListingFastResponse(RootModel):
78-
root: List[CheckoutFast]
77+
root: list[CheckoutFast]

backend/kernelCI_app/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def view_cache(view):
2525
path("test/<str:test_id>", view_cache(views.TestDetails), name="testDetails"),
2626
path("tree/", view_cache(views.TreeView), name="tree"),
2727
path("tree-fast/", view_cache(views.TreeViewFast), name="tree-fast"),
28+
path("tree/listing", view_cache(views.TreeListingView), name="treeListing"),
2829
path(
2930
"tree/<str:commit_hash>/full",
3031
views.TreeDetails.as_view(),
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
from django.http import HttpRequest
2+
from drf_spectacular.utils import extend_schema
3+
from kernelCI_app.constants.localization import ClientStrings
4+
from kernelCI_app.helpers.database import dict_fetchall
5+
from kernelCI_app.helpers.errorHandling import create_api_error_response
6+
from rest_framework.views import APIView
7+
from rest_framework.response import Response
8+
from kernelCI_app.helpers.trees import sanitize_tree
9+
from kernelCI_app.typeModels.commonListing import ListingQueryParameters
10+
from http import HTTPStatus
11+
from kernelCI_app.typeModels.treeListing import TreeListingResponse
12+
from pydantic import ValidationError
13+
14+
from django.db import connections
15+
16+
17+
# TODO: move to queries folder
18+
# TODO: add unit tests
19+
# TODO: stop using relative intervals!
20+
def get_new_tree_listing_data(origin: str, interval_in_days: int) -> list[dict]:
21+
"""
22+
Fetches data from the tree_listing table for direct use in the frontend.
23+
The status counts are NOT grouped in the return.
24+
"""
25+
26+
query = """
27+
SELECT
28+
id,
29+
_timestamp,
30+
checkout_id,
31+
origin,
32+
tree_name,
33+
git_repository_url,
34+
git_repository_branch,
35+
git_commit_hash,
36+
git_commit_name,
37+
git_commit_tags,
38+
start_time,
39+
origin_builds_finish_time,
40+
origin_tests_finish_time,
41+
42+
pass_builds,
43+
fail_builds,
44+
done_builds,
45+
miss_builds,
46+
skip_builds,
47+
error_builds,
48+
null_builds,
49+
50+
pass_boots,
51+
fail_boots,
52+
done_boots,
53+
miss_boots,
54+
skip_boots,
55+
error_boots,
56+
null_boots,
57+
58+
pass_tests,
59+
fail_tests,
60+
done_tests,
61+
miss_tests,
62+
skip_tests,
63+
error_tests,
64+
null_tests
65+
FROM
66+
tree_listing
67+
WHERE
68+
origin = %s
69+
AND start_time >= NOW() - INTERVAL '%s days'
70+
"""
71+
72+
params = [origin, interval_in_days]
73+
74+
with connections["default"].cursor() as cursor:
75+
cursor.execute(query, params)
76+
result = dict_fetchall(cursor)
77+
78+
return result
79+
80+
81+
class TreeListingView(APIView):
82+
@extend_schema(
83+
responses=TreeListingResponse,
84+
parameters=[ListingQueryParameters],
85+
methods=["GET"],
86+
)
87+
def get(self, request: HttpRequest) -> Response:
88+
"""
89+
Returns the checkout data for trees in a specific origin, in the last X days.
90+
The data includes the number of builds, boots and tests, grouped by status, for each checkout.
91+
92+
Query params (`ListingQueryParameters`):
93+
- origin: str
94+
- interval_in_days: int
95+
96+
Status returns:
97+
- 200: A list of checkouts with their respective data.
98+
- 400: Bad request, invalid parameters.
99+
- 500: Internal server error, something went wrong on the server. Usually validation.
100+
"""
101+
102+
try:
103+
request_params = ListingQueryParameters(
104+
origin=request.GET.get("origin"),
105+
interval_in_days=request.GET.get("interval_in_days"),
106+
)
107+
except ValidationError as e:
108+
return Response(data=e.json(), status=HTTPStatus.BAD_REQUEST)
109+
110+
rows = get_new_tree_listing_data(
111+
origin=request_params.origin,
112+
interval_in_days=request_params.interval_in_days,
113+
)
114+
115+
if not rows:
116+
return create_api_error_response(
117+
error_message=ClientStrings.NO_TREES_FOUND, status_code=HTTPStatus.OK
118+
)
119+
120+
try:
121+
valid_response = TreeListingResponse(
122+
root=[sanitize_tree(row) for row in rows]
123+
)
124+
except ValidationError as e:
125+
return Response(data=e.json(), status=HTTPStatus.INTERNAL_SERVER_ERROR)
126+
127+
return Response(valid_response.model_dump(by_alias=True))

backend/schema.yml

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,50 @@ paths:
15971597
schema:
15981598
$ref: '#/components/schemas/CommonDetailsTestsResponse'
15991599
description: ''
1600+
/api/tree/listing:
1601+
get:
1602+
operationId: tree_listing_retrieve
1603+
description: |-
1604+
Returns the checkout data for trees in a specific origin, in the last X days.
1605+
The data includes the number of builds, boots and tests, grouped by status, for each checkout.
1606+
1607+
Query params (`ListingQueryParameters`):
1608+
- origin: str
1609+
- interval_in_days: int
1610+
1611+
Status returns:
1612+
- 200: A list of checkouts with their respective data.
1613+
- 400: Bad request, invalid parameters.
1614+
- 500: Internal server error, something went wrong on the server. Usually validation.
1615+
parameters:
1616+
- in: query
1617+
name: interval_in_days
1618+
schema:
1619+
default: 7
1620+
exclusiveMinimum: 0
1621+
title: Interval In Days
1622+
type: integer
1623+
description: Interval in days for the listing
1624+
- in: query
1625+
name: origin
1626+
schema:
1627+
default: maestro
1628+
title: Origin
1629+
type: string
1630+
description: Origin filter
1631+
tags:
1632+
- tree
1633+
security:
1634+
- cookieAuth: []
1635+
- basicAuth: []
1636+
- {}
1637+
responses:
1638+
'200':
1639+
content:
1640+
application/json:
1641+
schema:
1642+
$ref: '#/components/schemas/TreeListingResponse'
1643+
description: ''
16001644
components:
16011645
schemas:
16021646
BuildArchitectures:
@@ -3288,7 +3332,7 @@ components:
32883332
build_id:
32893333
$ref: '#/components/schemas/Build__Id'
32903334
status:
3291-
$ref: '#/components/schemas/Test__Status'
3335+
$ref: '#/components/schemas/StatusValues'
32923336
path:
32933337
$ref: '#/components/schemas/Test__Path'
32943338
log_excerpt:

0 commit comments

Comments
 (0)