Skip to content

Commit 793b8ef

Browse files
feat(tree): add lazy loading for commit history endpoint (#1729)
1 parent 5a5f50f commit 793b8ef

7 files changed

Lines changed: 149 additions & 41 deletions

File tree

backend/kernelCI_app/queries/tree.py

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,13 @@ def get_tree_commit_history(
518518
git_url: Optional[str],
519519
git_branch: Optional[str],
520520
tree_name: Optional[str],
521+
include_types: Optional[list[str]] = None,
521522
) -> Optional[list[tuple]]:
523+
if not include_types:
524+
include_types = ["builds", "boots", "tests"]
525+
526+
include_types = [t.lower() for t in include_types]
527+
522528
field_values = {
523529
"commit_hash": commit_hash,
524530
"origin_param": origin,
@@ -541,6 +547,62 @@ def get_tree_commit_history(
541547
git_url=git_url, git_branch=git_branch, tree_name=tree_name
542548
)
543549

550+
include_builds = "builds" in include_types
551+
include_boots = "boots" in include_types
552+
include_tests = "tests" in include_types
553+
include_test_data = include_tests or include_boots
554+
555+
build_prefix = "b." if include_builds else "NULL AS "
556+
test_prefix = "t." if include_test_data else "NULL AS "
557+
build_id = "b.id" if include_builds else "NULL"
558+
build_misc = "b.misc" if include_builds else "NULL"
559+
test_misc_runtime = "t.misc->>'runtime'" if include_test_data else "NULL"
560+
test_id = "t.id" if include_test_data else "NULL"
561+
562+
select_clause = f"""c.git_commit_hash,
563+
c.git_commit_name,
564+
c.git_commit_tags,
565+
c.start_time,
566+
{build_prefix}duration,
567+
{build_prefix}architecture,
568+
{build_prefix}compiler,
569+
{build_prefix}config_name,
570+
{build_prefix}status,
571+
{build_prefix}origin,
572+
{build_id} AS build_id,
573+
{build_misc} AS build_misc,
574+
{test_prefix}path,
575+
{test_prefix}status,
576+
{test_prefix}duration,
577+
{test_prefix}environment_compatible,
578+
{test_prefix}environment_misc,
579+
{test_prefix}origin,
580+
{test_misc_runtime} AS test_lab,
581+
{test_id} AS test_id,
582+
ic.id AS incidents_id,
583+
ic.test_id AS incidents_test_id,
584+
i.id AS issues_id,
585+
i.version AS issues_version"""
586+
587+
if include_boots and not include_tests:
588+
test_filter = "AND (t.path IS NULL OR t.path LIKE 'boot%%')"
589+
elif include_tests and not include_boots:
590+
test_filter = "AND (t.path IS NULL OR t.path NOT LIKE 'boot%%')"
591+
else:
592+
test_filter = ""
593+
594+
if include_test_data:
595+
test_join = f"LEFT JOIN tests AS t ON t.build_id = b.id {test_filter}"
596+
incidents_condition = "t.id = ic.test_id OR b.id = ic.build_id"
597+
else:
598+
test_join = ""
599+
incidents_condition = "b.id = ic.build_id"
600+
601+
join_clause = f"""LEFT JOIN builds AS b ON c.id = b.checkout_id
602+
{test_join}
603+
LEFT JOIN incidents AS ic ON {incidents_condition}
604+
LEFT JOIN issues AS i ON ic.issue_id = i.id"""
605+
544606
query = f"""
545607
WITH HEAD_START_TIME AS (
546608
SELECT
@@ -630,37 +692,10 @@ def get_tree_commit_history(
630692
c.start_time DESC
631693
)
632694
SELECT
633-
c.git_commit_hash,
634-
c.git_commit_name,
635-
c.git_commit_tags,
636-
c.start_time,
637-
b.duration,
638-
b.architecture,
639-
b.compiler,
640-
b.config_name,
641-
b.status,
642-
b.origin,
643-
t.path,
644-
t.status,
645-
t.duration,
646-
t.environment_compatible,
647-
t.environment_misc,
648-
t.origin,
649-
t.misc->>'runtime' AS test_lab,
650-
b.id AS build_id,
651-
b.misc AS build_misc,
652-
t.id AS test_id,
653-
ic.id AS incidents_id,
654-
ic.test_id AS incidents_test_id,
655-
i.id AS issues_id,
656-
i.version AS issues_version
695+
{select_clause}
657696
FROM
658697
SELECTED_CHECKOUTS AS c
659-
LEFT JOIN builds AS b ON c.id = b.checkout_id
660-
LEFT JOIN tests AS t ON t.build_id = b.id
661-
LEFT JOIN incidents AS ic ON t.id = ic.test_id
662-
OR b.id = ic.build_id
663-
LEFT JOIN issues AS i ON ic.issue_id = i.id
698+
{join_clause}
664699
"""
665700

666701
with connection.cursor() as cursor:

backend/kernelCI_app/typeModels/treeCommits.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime
2+
from enum import Enum
23
from typing import List, Optional
3-
from pydantic import BaseModel, RootModel
4+
from pydantic import BaseModel, RootModel, field_validator
45

56
from kernelCI_app.constants.general import DEFAULT_ORIGIN
67
from kernelCI_app.constants.localization import DocStrings
@@ -14,6 +15,12 @@
1415
)
1516

1617

18+
class TreeEntityTypes(str, Enum):
19+
BUILDS = "builds"
20+
BOOTS = "boots"
21+
TESTS = "tests"
22+
23+
1724
class DirectTreeCommitsQueryParameters(BaseModel):
1825
origin: str = Field(
1926
DEFAULT_ORIGIN, description=DocStrings.TREE_COMMIT_ORIGIN_DESCRIPTION
@@ -24,8 +31,21 @@ class DirectTreeCommitsQueryParameters(BaseModel):
2431
end_time_stamp_in_seconds: Optional[str] = Field(
2532
None, description=DocStrings.TREE_COMMIT_END_TS_DESCRIPTION
2633
)
34+
types: Optional[list[TreeEntityTypes]] = Field(
35+
None,
36+
description="List of types to include (builds, boots, tests)",
37+
)
2738
# TODO: Add filters field in this model
2839

40+
@field_validator("types", mode="before")
41+
@classmethod
42+
def validate_types(cls, value):
43+
if not value:
44+
return []
45+
if isinstance(value, str):
46+
value = [t.strip() for t in value.split(",") if t.strip()]
47+
return value
48+
2949

3050
class TreeCommitsQueryParameters(DirectTreeCommitsQueryParameters):
3151
git_branch: Optional[str] = Field(

backend/kernelCI_app/views/treeCommitsHistory.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def setup_filters(self):
6868
def sanitize_rows(self, rows: dict) -> list:
6969
result = []
7070
for row in rows:
71-
build_misc = row[18]
71+
build_misc = row[11]
7272
sanitized_build_misc = sanitize_dict(build_misc)
7373
build_lab = (
7474
sanitized_build_misc.get("lab", UNKNOWN_STRING)
@@ -88,15 +88,15 @@ def sanitize_rows(self, rows: dict) -> list:
8888
"config_name": row[7],
8989
"build_status": NULL_STATUS if row[8] is None else row[8],
9090
"build_origin": row[9],
91-
"test_path": row[10],
92-
"test_status": row[11],
93-
"test_duration": row[12],
94-
"hardware_compatibles": row[13],
95-
"test_environment_misc": row[14],
96-
"test_origin": row[15],
97-
"test_lab": row[16],
98-
"build_id": row[17],
91+
"build_id": row[10],
9992
"build_misc": build_misc,
93+
"test_path": row[12],
94+
"test_status": row[13],
95+
"test_duration": row[14],
96+
"hardware_compatibles": row[15],
97+
"test_environment_misc": row[16],
98+
"test_origin": row[17],
99+
"test_lab": row[18],
100100
"test_id": row[19],
101101
"incidents_id": row[20],
102102
"incidents_test_id": row[21],
@@ -403,6 +403,7 @@ def get(
403403
"start_timestamp_in_seconds"
404404
),
405405
end_timestamp_in_seconds=request.GET.get("end_timestamp_in_seconds"),
406+
types=request.GET.get("types"),
406407
)
407408
except ValidationError as e:
408409
return create_api_error_response(
@@ -430,6 +431,7 @@ def get(
430431
git_url=params.git_url,
431432
git_branch=params.git_branch or git_branch,
432433
tree_name=tree_name,
434+
include_types=params.types,
433435
)
434436

435437
if not rows:

backend/schema.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,17 @@ paths:
12071207
default: null
12081208
title: Start Time Stamp In Seconds
12091209
description: Start time filter in seconds for tree commits
1210+
- in: query
1211+
name: types
1212+
schema:
1213+
anyOf:
1214+
- items:
1215+
$ref: '#/components/schemas/TreeEntityTypes'
1216+
type: array
1217+
- type: 'null'
1218+
default: null
1219+
title: Types
1220+
description: List of types to include (builds, boots, tests)
12101221
tags:
12111222
- tree
12121223
security:
@@ -1528,6 +1539,17 @@ paths:
15281539
type: string
15291540
description: Name of the tree
15301541
required: true
1542+
- in: query
1543+
name: types
1544+
schema:
1545+
anyOf:
1546+
- items:
1547+
$ref: '#/components/schemas/TreeEntityTypes'
1548+
type: array
1549+
- type: 'null'
1550+
default: null
1551+
title: Types
1552+
description: List of types to include (builds, boots, tests)
15311553
tags:
15321554
- tree
15331555
security:
@@ -3976,6 +3998,13 @@ components:
39763998
- builds
39773999
title: TreeDetailsFullResponse
39784000
type: object
4001+
TreeEntityTypes:
4002+
enum:
4003+
- builds
4004+
- boots
4005+
- tests
4006+
title: TreeEntityTypes
4007+
type: string
39794008
TreeLatestResponse:
39804009
properties:
39814010
git_repository_url:

dashboard/src/api/commitHistory.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
import { mapFiltersKeysToBackendCompatible } from '@/utils/utils';
1212

1313
import { getTargetFilter } from '@/types/general';
14-
import type { TFilter } from '@/types/general';
14+
import type { TFilter, TreeEntityTypes } from '@/types/general';
1515

1616
import { RequestData } from './commonRequest';
1717

@@ -25,6 +25,7 @@ const fetchCommitHistory = async (
2525
endTimestampInSeconds: number | undefined,
2626
treeName?: string,
2727
treeUrlFrom?: TreeDetailsRouteFrom,
28+
types?: TreeEntityTypes[],
2829
): Promise<TTreeCommitHistoryResponse> => {
2930
const filtersFormatted = mapFiltersKeysToBackendCompatible(filters);
3031

@@ -34,6 +35,7 @@ const fetchCommitHistory = async (
3435
git_branch: gitBranch,
3536
start_time_stamp_in_seconds: startTimestampInSeconds,
3637
end_time_stamp_in_seconds: endTimestampInSeconds,
38+
types: types?.join(','),
3739
...filtersFormatted,
3840
};
3941

@@ -62,6 +64,7 @@ export const useCommitHistory = ({
6264
startTimestampInSeconds,
6365
treeName,
6466
treeUrlFrom,
67+
types,
6568
}: {
6669
commitHash: string;
6770
origin: string;
@@ -72,6 +75,7 @@ export const useCommitHistory = ({
7275
endTimestampInSeconds?: number;
7376
treeName?: string;
7477
treeUrlFrom?: TreeDetailsRouteFrom;
78+
types?: TreeEntityTypes[];
7579
}): UseQueryResult<TTreeCommitHistoryResponse> => {
7680
const testFilter = getTargetFilter(filter, 'test');
7781
const treeDetailsFilter = getTargetFilter(filter, 'treeDetails');
@@ -93,6 +97,7 @@ export const useCommitHistory = ({
9397
endTimestampInSeconds,
9498
treeName,
9599
treeUrlFrom,
100+
types,
96101
],
97102
queryFn: () =>
98103
fetchCommitHistory(
@@ -105,6 +110,7 @@ export const useCommitHistory = ({
105110
endTimestampInSeconds,
106111
treeName,
107112
treeUrlFrom,
113+
types,
108114
),
109115
});
110116
};

dashboard/src/components/CommitNavigationGraph/CommitNavigationGraph.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { MessagesKey } from '@/locales/messages';
1313
import { formatDate } from '@/utils/utils';
1414
import { mapFilterToReq } from '@/components/Tabs/Filters';
1515
import { useCommitHistory } from '@/api/commitHistory';
16-
import type { TFilter } from '@/types/general';
16+
import type { TFilter, TreeEntityTypes } from '@/types/general';
1717

1818
import { MemoizedSectionError } from '@/components/DetailsPages/SectionError';
1919

@@ -74,6 +74,19 @@ const CommitNavigationGraph = ({
7474

7575
const reqFilter = mapFilterToReq(diffFilter);
7676

77+
const types: TreeEntityTypes[] = useMemo(() => {
78+
switch (currentPageTab) {
79+
case 'global.builds':
80+
return ['builds'];
81+
case 'global.boots':
82+
return ['boots'];
83+
case 'global.tests':
84+
return ['tests'];
85+
default:
86+
return ['builds'];
87+
}
88+
}, [currentPageTab]);
89+
7790
const { data, status, error, isLoading } = useCommitHistory({
7891
gitBranch: gitBranch ?? '',
7992
gitUrl: gitUrl ?? '',
@@ -84,6 +97,7 @@ const CommitNavigationGraph = ({
8497
startTimestampInSeconds,
8598
treeName,
8699
treeUrlFrom,
100+
types,
87101
});
88102

89103
const displayableData = data ? data : null;

dashboard/src/types/general.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,5 @@ export type PossibleMonitorPath =
398398
| '/hardware/v1'
399399
| '/tree/v1'
400400
| '/tree/v2';
401+
402+
export type TreeEntityTypes = 'builds' | 'boots' | 'tests';

0 commit comments

Comments
 (0)