Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 4cc54fd

Browse files
committed
Update path contents to reflect path content connection, create new array paginator
1 parent f76dba5 commit 4cc54fd

File tree

3 files changed

+216
-17
lines changed

3 files changed

+216
-17
lines changed

graphql_api/helpers/connection.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,42 +84,50 @@ def __init__(
8484
self.start_index = 0
8585
self.end_index = len(data)
8686

87-
if last and first:
88-
raise ValueError("Cannot provide both last and first")
87+
if first and last:
88+
raise ValueError("Cannot provide both 'first' and 'last'")
8989

9090
# Handle 'after' cursor
9191
if after is not None:
9292
self.start_index = int(after) + 1
9393

94-
# Apply first/last pagination
95-
if first:
96-
self.end_index = min(self.start_index + first, len(data))
97-
if last:
98-
self.start_index = max(len(data) - last, 0)
99-
self.end_index = len(data)
100-
10194
# Handle 'before' cursor
10295
if before is not None:
103-
self.start_index = len(data) - (int(last) + 1)
104-
self.end_index = int(before)
96+
self.end_index = min(self.end_index, int(before))
97+
98+
# Ensure valid bounds after 'after' and 'before'
99+
self.start_index = max(self.start_index, 0)
100+
self.end_index = min(self.end_index, len(data))
101+
102+
if first is not None:
103+
self.end_index = min(self.start_index + first, len(data))
104+
105+
if last is not None:
106+
range_length = self.end_index - self.start_index
107+
if range_length > last:
108+
self.start_index = self.end_index - last
109+
110+
# Ensure bounds remain valid
111+
self.start_index = max(self.start_index, 0)
112+
self.end_index = min(self.end_index, len(data))
105113

106114
def cursor(self, position: int) -> str:
107-
"""Generate a cursor based on the position (index)"""
115+
"""Generate a cursor based on the position (index)."""
108116
return str(position)
109117

110118
@property
111119
def page(self) -> List[Any]:
112-
"""Returns the sliced page of data"""
120+
"""Returns the sliced page of data."""
113121
return self.data[self.start_index : self.end_index]
114122

115123
@property
116124
def has_next(self) -> bool:
117-
"""Check if there's a next page"""
125+
"""Check if there's a next page."""
118126
return self.end_index < len(self.data)
119127

120128
@property
121129
def has_previous(self) -> bool:
122-
"""Check if there's a previous page"""
130+
"""Check if there's a previous page."""
123131
return self.start_index > 0
124132

125133

graphql_api/helpers/tests/test_connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,14 @@ def test_array_pagination_edge_cases_with_before_cursor_2(self):
126126
data = [1, 2, 3, 4, 5]
127127

128128
connection = queryset_to_connection_sync(data, last=3, before="3")
129-
self.assertEqual([edge["node"] for edge in connection.edges], [2, 3])
129+
self.assertEqual([edge["node"] for edge in connection.edges], [1, 2, 3])
130130

131131
def test_array_pagination_edge_cases_with_before_and_after(self):
132132
from graphql_api.helpers.connection import queryset_to_connection_sync
133133

134134
data = [1, 2, 3, 4, 5]
135135

136-
connection = queryset_to_connection_sync(data, last=3, before="3", after="1")
136+
connection = queryset_to_connection_sync(data, last=3, before="3", after="0")
137137
self.assertEqual([edge["node"] for edge in connection.edges], [2, 3])
138138

139139
def test_both_first_and_last(self):

graphql_api/tests/test_branch.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,61 @@
7474
}
7575
"""
7676

77+
query_files_connection = """
78+
query FetchFiles($org: String!, $repo: String!, $branch: String!, $path: String!, $filters: PathContentsFilters!, $first: Int, $after: String, $last: Int, $before: String) {
79+
owner(username: $org) {
80+
repository(name: $repo) {
81+
... on Repository {
82+
branch(name: $branch) {
83+
head {
84+
deprecatedPathContents (path: $path, filters: $filters, first: $first, after: $after, last: $last, before: $before) {
85+
__typename
86+
... on PathContentConnection {
87+
edges {
88+
cursor
89+
node {
90+
__typename
91+
name
92+
path
93+
hits
94+
misses
95+
partials
96+
lines
97+
percentCovered
98+
... on PathContentFile {
99+
isCriticalFile
100+
}
101+
}
102+
}
103+
totalCount
104+
pageInfo {
105+
hasNextPage
106+
hasPreviousPage
107+
startCursor
108+
endCursor
109+
}
110+
}
111+
... on MissingHeadReport {
112+
message
113+
}
114+
... on MissingCoverage {
115+
message
116+
}
117+
... on UnknownPath {
118+
message
119+
}
120+
... on UnknownFlags {
121+
message
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}
128+
}
129+
}
130+
"""
131+
77132

78133
class MockCoverage(object):
79134
def __init__(self, coverage, hits, lines):
@@ -1084,3 +1139,139 @@ def test_fetch_path_contents_component_flags_filters(
10841139
}
10851140
}
10861141
}
1142+
1143+
@patch(
1144+
"services.profiling.ProfilingSummary.critical_files", new_callable=PropertyMock
1145+
)
1146+
@patch("shared.reports.api_report_service.build_report_from_commit")
1147+
def test_fetch_path_contents_deprecated(self, report_mock, critical_files_mock):
1148+
report_mock.return_value = MockReport()
1149+
critical_files_mock.return_value = []
1150+
1151+
variables = {
1152+
"org": self.org.username,
1153+
"repo": self.repo.name,
1154+
"branch": self.branch.name,
1155+
"path": "",
1156+
"filters": {},
1157+
}
1158+
1159+
data = self.gql_request(query_files, variables=variables)
1160+
assert data == {
1161+
"owner": {
1162+
"repository": {
1163+
"branch": {
1164+
"head": {
1165+
"pathContents": {
1166+
"__typename": "PathContents",
1167+
"results": [
1168+
{
1169+
"__typename": "PathContentFile",
1170+
"name": "fileA.py",
1171+
"path": "fileA.py",
1172+
"hits": 8,
1173+
"misses": 0,
1174+
"partials": 0,
1175+
"lines": 10,
1176+
"percentCovered": 80.0,
1177+
"isCriticalFile": False,
1178+
},
1179+
{
1180+
"__typename": "PathContentFile",
1181+
"name": "fileB.py",
1182+
"path": "fileB.py",
1183+
"hits": 8,
1184+
"misses": 0,
1185+
"partials": 0,
1186+
"lines": 10,
1187+
"percentCovered": 80.0,
1188+
"isCriticalFile": False,
1189+
},
1190+
{
1191+
"__typename": "PathContentDir",
1192+
"name": "folder",
1193+
"path": "folder",
1194+
"hits": 24,
1195+
"misses": 0,
1196+
"partials": 0,
1197+
"lines": 30,
1198+
"percentCovered": 80.0,
1199+
},
1200+
],
1201+
}
1202+
}
1203+
}
1204+
}
1205+
}
1206+
}
1207+
1208+
@patch(
1209+
"services.profiling.ProfilingSummary.critical_files", new_callable=PropertyMock
1210+
)
1211+
@patch("shared.reports.api_report_service.build_report_from_commit")
1212+
def test_fetch_path_contents_deprecated_paginated(
1213+
self, report_mock, critical_files_mock
1214+
):
1215+
report_mock.return_value = MockReport()
1216+
critical_files_mock.return_value = []
1217+
1218+
variables = {
1219+
"org": self.org.username,
1220+
"repo": self.repo.name,
1221+
"branch": self.branch.name,
1222+
"path": "",
1223+
"filters": {},
1224+
"first": 2,
1225+
}
1226+
1227+
data = self.gql_request(query_files_connection, variables=variables)
1228+
assert data == {
1229+
"owner": {
1230+
"repository": {
1231+
"branch": {
1232+
"head": {
1233+
"deprecatedPathContents": {
1234+
"__typename": "PathContentConnection",
1235+
"edges": [
1236+
{
1237+
"cursor": "0",
1238+
"node": {
1239+
"__typename": "PathContentFile",
1240+
"name": "fileA.py",
1241+
"path": "fileA.py",
1242+
"hits": 8,
1243+
"misses": 0,
1244+
"partials": 0,
1245+
"lines": 10,
1246+
"percentCovered": 80.0,
1247+
"isCriticalFile": False,
1248+
},
1249+
},
1250+
{
1251+
"cursor": "1",
1252+
"node": {
1253+
"__typename": "PathContentFile",
1254+
"name": "fileB.py",
1255+
"path": "fileB.py",
1256+
"hits": 8,
1257+
"misses": 0,
1258+
"partials": 0,
1259+
"lines": 10,
1260+
"percentCovered": 80.0,
1261+
"isCriticalFile": False,
1262+
},
1263+
},
1264+
],
1265+
"totalCount": 3,
1266+
"pageInfo": {
1267+
"hasNextPage": True,
1268+
"hasPreviousPage": False,
1269+
"startCursor": "0",
1270+
"endCursor": "1",
1271+
},
1272+
}
1273+
}
1274+
}
1275+
}
1276+
}
1277+
}

0 commit comments

Comments
 (0)