Skip to content

Commit 9644107

Browse files
fix(api): add pagination to project endpoints (#2349)
fix(api): add pagination to project endpoints No pagination was present with some of the endpoints which resulted in the duplicate data being presented in the views on dashboard. TODO: Write new tests or update the old ones to cover new functionality. Update doc-strings where appropriate. Update or write new documentation in packit/packit.dev. Fixes packit/dashboard#376 RELEASE NOTES BEGIN We have fixed an issue that caused loading the same data multiple times on the dashboard within the project views. RELEASE NOTES END Reviewed-by: Laura Barcziová
2 parents 1153d33 + b297d48 commit 9644107

File tree

3 files changed

+69
-23
lines changed

3 files changed

+69
-23
lines changed

packit_service/models.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,14 @@ def get_by_forge(
532532

533533
@classmethod
534534
def get_by_forge_namespace(
535-
cls, forge: str, namespace: str
535+
cls, first: int, last: int, forge: str, namespace: str
536536
) -> Iterable["GitProjectModel"]:
537537
"""Return projects of given forge and namespace"""
538538
with sa_session_transaction() as session:
539-
return session.query(GitProjectModel).filter_by(
540-
instance_url=forge, namespace=namespace
539+
return (
540+
session.query(GitProjectModel)
541+
.filter_by(instance_url=forge, namespace=namespace)
542+
.slice(first, last)
541543
)
542544

543545
@classmethod
@@ -571,7 +573,7 @@ def get_project_prs(
571573

572574
@classmethod
573575
def get_project_issues(
574-
cls, forge: str, namespace: str, repo_name: str
576+
cls, first: int, last: int, forge: str, namespace: str, repo_name: str
575577
) -> Iterable["IssueModel"]:
576578
with sa_session_transaction() as session:
577579
return (
@@ -582,11 +584,12 @@ def get_project_issues(
582584
GitProjectModel.namespace == namespace,
583585
GitProjectModel.repo_name == repo_name,
584586
)
587+
.slice(first, last)
585588
)
586589

587590
@classmethod
588591
def get_project_branches(
589-
cls, forge: str, namespace: str, repo_name: str
592+
cls, first: int, last: int, forge: str, namespace: str, repo_name: str
590593
) -> Iterable["GitBranchModel"]:
591594
with sa_session_transaction() as session:
592595
return (
@@ -597,11 +600,12 @@ def get_project_branches(
597600
GitProjectModel.namespace == namespace,
598601
GitProjectModel.repo_name == repo_name,
599602
)
603+
.slice(first, last)
600604
)
601605

602606
@classmethod
603607
def get_project_releases(
604-
cls, forge: str, namespace: str, repo_name: str
608+
cls, first: int, last: int, forge: str, namespace: str, repo_name: str
605609
) -> Iterable["ProjectReleaseModel"]:
606610
with sa_session_transaction() as session:
607611
return (
@@ -612,6 +616,7 @@ def get_project_releases(
612616
GitProjectModel.namespace == namespace,
613617
GitProjectModel.repo_name == repo_name,
614618
)
619+
.slice(first, last)
615620
)
616621

617622
# ACTIVE PROJECTS

packit_service/service/api/projects.py

+54-13
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,16 @@ def get(self, forge):
111111
@ns.param("forge", "Git Forge")
112112
@ns.param("namespace", "Namespace")
113113
class ProjectsNamespace(Resource):
114+
@ns.expect(pagination_arguments)
114115
@ns.response(HTTPStatus.OK.value, "Projects details follow")
115116
def get(self, forge, namespace):
116117
"""List of projects of given forge and namespace"""
117118
result = []
118-
for project in GitProjectModel.get_by_forge_namespace(forge, namespace):
119+
first, last = indices()
120+
121+
for project in GitProjectModel.get_by_forge_namespace(
122+
first, last, forge, namespace
123+
):
119124
project_info = {
120125
"namespace": project.namespace,
121126
"repo_name": project.repo_name,
@@ -126,7 +131,13 @@ def get(self, forge, namespace):
126131
"issues_handled": len(project.issues),
127132
}
128133
result.append(project_info)
129-
return response_maker(result)
134+
135+
resp = response_maker(
136+
result,
137+
status=HTTPStatus.PARTIAL_CONTENT if result else HTTPStatus.OK,
138+
)
139+
resp.headers["Content-Range"] = f"git-projects {first + 1}-{last}/*"
140+
return resp
130141

131142

132143
@ns.route("/<forge>/<namespace>/<repo_name>/prs")
@@ -207,55 +218,79 @@ def get(self, forge, namespace, repo_name):
207218
@ns.param("namespace", "Namespace")
208219
@ns.param("repo_name", "Repo Name")
209220
class ProjectIssues(Resource):
221+
@ns.expect(pagination_arguments)
210222
@ns.response(
211223
HTTPStatus.OK.value, "OK, project issues handled by Packit Service follow"
212224
)
213225
def get(self, forge, namespace, repo_name):
214226
"""Project issues"""
215-
return response_maker(
216-
[
217-
issue.issue_id
218-
for issue in GitProjectModel.get_project_issues(
219-
forge, namespace, repo_name
220-
)
221-
]
227+
first, last = indices()
228+
229+
issues = [
230+
issue.issue_id
231+
for issue in GitProjectModel.get_project_issues(
232+
first, last, forge, namespace, repo_name
233+
)
234+
]
235+
236+
resp = response_maker(
237+
issues,
238+
status=HTTPStatus.PARTIAL_CONTENT if issues else HTTPStatus.OK,
222239
)
223240

241+
resp.headers["Content-Range"] = f"git-project-issues {first + 1}-{last}/*"
242+
return resp
243+
224244

225245
@ns.route("/<forge>/<namespace>/<repo_name>/releases")
226246
@ns.param("forge", "Git Forge")
227247
@ns.param("namespace", "Namespace")
228248
@ns.param("repo_name", "Repo Name")
229249
class ProjectReleases(Resource):
250+
@ns.expect(pagination_arguments)
230251
@ns.response(
231252
HTTPStatus.OK.value, "OK, project releases handled by Packit Service follow"
232253
)
233254
def get(self, forge, namespace, repo_name):
234255
"""Project releases"""
235256
result = []
257+
first, last = indices()
258+
236259
for release in GitProjectModel.get_project_releases(
237-
forge, namespace, repo_name
260+
first, last, forge, namespace, repo_name
238261
):
239262
release_info = {
240263
"tag_name": release.tag_name,
241264
"commit_hash": release.commit_hash,
242265
}
243266
result.append(release_info)
244-
return response_maker(result)
267+
268+
resp = response_maker(
269+
result,
270+
status=HTTPStatus.PARTIAL_CONTENT if result else HTTPStatus.OK,
271+
)
272+
273+
resp.headers["Content-Range"] = f"git-project-releases {first + 1}-{last}/*"
274+
return resp
245275

246276

247277
@ns.route("/<forge>/<namespace>/<repo_name>/branches")
248278
@ns.param("forge", "Git Forge")
249279
@ns.param("namespace", "Namespace")
250280
@ns.param("repo_name", "Repo Name")
251281
class ProjectBranches(Resource):
282+
@ns.expect(pagination_arguments)
252283
@ns.response(
253284
HTTPStatus.OK.value, "OK, project branches handled by Packit Service follow"
254285
)
255286
def get(self, forge, namespace, repo_name):
256287
"""Project branches"""
257288
result = []
258-
for branch in GitProjectModel.get_project_branches(forge, namespace, repo_name):
289+
first, last = indices()
290+
291+
for branch in GitProjectModel.get_project_branches(
292+
first, last, forge, namespace, repo_name
293+
):
259294
branch_info = {
260295
"branch": branch.name,
261296
"builds": [],
@@ -300,4 +335,10 @@ def get(self, forge, namespace, repo_name):
300335
branch_info["tests"].append(test_info)
301336
result.append(branch_info)
302337

303-
return response_maker(result)
338+
resp = response_maker(
339+
result,
340+
status=HTTPStatus.PARTIAL_CONTENT if result else HTTPStatus.OK,
341+
)
342+
343+
resp.headers["Content-Range"] = f"git-project-branches {first + 1}-{last}/*"
344+
return resp

tests_openshift/database/test_models.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ def test_get_by_forge(clean_before_and_after, multiple_forge_projects):
684684

685685
def test_get_by_forge_namespace(clean_before_and_after, multiple_copr_builds):
686686
projects = list(
687-
GitProjectModel.get_by_forge_namespace("github.com", "the-namespace")
687+
GitProjectModel.get_by_forge_namespace(0, 10, "github.com", "the-namespace")
688688
)
689689
assert projects[0].namespace == "the-namespace"
690690
assert projects[0].repo_name == "the-repo-name"
@@ -716,7 +716,7 @@ def test_get_project_prs(clean_before_and_after, a_copr_build_for_pr):
716716
def test_get_project_branch(clean_before_and_after, a_copr_build_for_branch_push):
717717
branches_list = list(
718718
GitProjectModel.get_project_branches(
719-
"github.com", "the-namespace", "the-repo-name"
719+
0, 10, "github.com", "the-namespace", "the-repo-name"
720720
)
721721
)
722722
assert len(branches_list) == 1
@@ -726,7 +726,7 @@ def test_get_project_branch(clean_before_and_after, a_copr_build_for_branch_push
726726
def test_get_project_issues(clean_before_and_after, an_issue_model):
727727
issues_list = list(
728728
GitProjectModel.get_project_issues(
729-
"github.com", "the-namespace", "the-repo-name"
729+
0, 10, "github.com", "the-namespace", "the-repo-name"
730730
)
731731
)
732732
assert len(issues_list) == 1
@@ -736,7 +736,7 @@ def test_get_project_issues(clean_before_and_after, an_issue_model):
736736
def test_get_project_releases(clean_before_and_after, release_model):
737737
releases = list(
738738
GitProjectModel.get_project_releases(
739-
"github.com", "the-namespace", "the-repo-name"
739+
0, 10, "github.com", "the-namespace", "the-repo-name"
740740
)
741741
)
742742
assert releases[0].tag_name == "v1.0.2"

0 commit comments

Comments
 (0)