Skip to content

Commit

Permalink
fix(api): add pagination to project endpoints (#2349)
Browse files Browse the repository at this point in the history
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á
  • Loading branch information
softwarefactory-project-zuul[bot] authored Feb 15, 2024
2 parents 1153d33 + b297d48 commit 9644107
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 23 deletions.
17 changes: 11 additions & 6 deletions packit_service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,12 +532,14 @@ def get_by_forge(

@classmethod
def get_by_forge_namespace(
cls, forge: str, namespace: str
cls, first: int, last: int, forge: str, namespace: str
) -> Iterable["GitProjectModel"]:
"""Return projects of given forge and namespace"""
with sa_session_transaction() as session:
return session.query(GitProjectModel).filter_by(
instance_url=forge, namespace=namespace
return (
session.query(GitProjectModel)
.filter_by(instance_url=forge, namespace=namespace)
.slice(first, last)
)

@classmethod
Expand Down Expand Up @@ -571,7 +573,7 @@ def get_project_prs(

@classmethod
def get_project_issues(
cls, forge: str, namespace: str, repo_name: str
cls, first: int, last: int, forge: str, namespace: str, repo_name: str
) -> Iterable["IssueModel"]:
with sa_session_transaction() as session:
return (
Expand All @@ -582,11 +584,12 @@ def get_project_issues(
GitProjectModel.namespace == namespace,
GitProjectModel.repo_name == repo_name,
)
.slice(first, last)
)

@classmethod
def get_project_branches(
cls, forge: str, namespace: str, repo_name: str
cls, first: int, last: int, forge: str, namespace: str, repo_name: str
) -> Iterable["GitBranchModel"]:
with sa_session_transaction() as session:
return (
Expand All @@ -597,11 +600,12 @@ def get_project_branches(
GitProjectModel.namespace == namespace,
GitProjectModel.repo_name == repo_name,
)
.slice(first, last)
)

@classmethod
def get_project_releases(
cls, forge: str, namespace: str, repo_name: str
cls, first: int, last: int, forge: str, namespace: str, repo_name: str
) -> Iterable["ProjectReleaseModel"]:
with sa_session_transaction() as session:
return (
Expand All @@ -612,6 +616,7 @@ def get_project_releases(
GitProjectModel.namespace == namespace,
GitProjectModel.repo_name == repo_name,
)
.slice(first, last)
)

# ACTIVE PROJECTS
Expand Down
67 changes: 54 additions & 13 deletions packit_service/service/api/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,16 @@ def get(self, forge):
@ns.param("forge", "Git Forge")
@ns.param("namespace", "Namespace")
class ProjectsNamespace(Resource):
@ns.expect(pagination_arguments)
@ns.response(HTTPStatus.OK.value, "Projects details follow")
def get(self, forge, namespace):
"""List of projects of given forge and namespace"""
result = []
for project in GitProjectModel.get_by_forge_namespace(forge, namespace):
first, last = indices()

for project in GitProjectModel.get_by_forge_namespace(
first, last, forge, namespace
):
project_info = {
"namespace": project.namespace,
"repo_name": project.repo_name,
Expand All @@ -126,7 +131,13 @@ def get(self, forge, namespace):
"issues_handled": len(project.issues),
}
result.append(project_info)
return response_maker(result)

resp = response_maker(
result,
status=HTTPStatus.PARTIAL_CONTENT if result else HTTPStatus.OK,
)
resp.headers["Content-Range"] = f"git-projects {first + 1}-{last}/*"
return resp


@ns.route("/<forge>/<namespace>/<repo_name>/prs")
Expand Down Expand Up @@ -207,55 +218,79 @@ def get(self, forge, namespace, repo_name):
@ns.param("namespace", "Namespace")
@ns.param("repo_name", "Repo Name")
class ProjectIssues(Resource):
@ns.expect(pagination_arguments)
@ns.response(
HTTPStatus.OK.value, "OK, project issues handled by Packit Service follow"
)
def get(self, forge, namespace, repo_name):
"""Project issues"""
return response_maker(
[
issue.issue_id
for issue in GitProjectModel.get_project_issues(
forge, namespace, repo_name
)
]
first, last = indices()

issues = [
issue.issue_id
for issue in GitProjectModel.get_project_issues(
first, last, forge, namespace, repo_name
)
]

resp = response_maker(
issues,
status=HTTPStatus.PARTIAL_CONTENT if issues else HTTPStatus.OK,
)

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


@ns.route("/<forge>/<namespace>/<repo_name>/releases")
@ns.param("forge", "Git Forge")
@ns.param("namespace", "Namespace")
@ns.param("repo_name", "Repo Name")
class ProjectReleases(Resource):
@ns.expect(pagination_arguments)
@ns.response(
HTTPStatus.OK.value, "OK, project releases handled by Packit Service follow"
)
def get(self, forge, namespace, repo_name):
"""Project releases"""
result = []
first, last = indices()

for release in GitProjectModel.get_project_releases(
forge, namespace, repo_name
first, last, forge, namespace, repo_name
):
release_info = {
"tag_name": release.tag_name,
"commit_hash": release.commit_hash,
}
result.append(release_info)
return response_maker(result)

resp = response_maker(
result,
status=HTTPStatus.PARTIAL_CONTENT if result else HTTPStatus.OK,
)

resp.headers["Content-Range"] = f"git-project-releases {first + 1}-{last}/*"
return resp


@ns.route("/<forge>/<namespace>/<repo_name>/branches")
@ns.param("forge", "Git Forge")
@ns.param("namespace", "Namespace")
@ns.param("repo_name", "Repo Name")
class ProjectBranches(Resource):
@ns.expect(pagination_arguments)
@ns.response(
HTTPStatus.OK.value, "OK, project branches handled by Packit Service follow"
)
def get(self, forge, namespace, repo_name):
"""Project branches"""
result = []
for branch in GitProjectModel.get_project_branches(forge, namespace, repo_name):
first, last = indices()

for branch in GitProjectModel.get_project_branches(
first, last, forge, namespace, repo_name
):
branch_info = {
"branch": branch.name,
"builds": [],
Expand Down Expand Up @@ -300,4 +335,10 @@ def get(self, forge, namespace, repo_name):
branch_info["tests"].append(test_info)
result.append(branch_info)

return response_maker(result)
resp = response_maker(
result,
status=HTTPStatus.PARTIAL_CONTENT if result else HTTPStatus.OK,
)

resp.headers["Content-Range"] = f"git-project-branches {first + 1}-{last}/*"
return resp
8 changes: 4 additions & 4 deletions tests_openshift/database/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ def test_get_by_forge(clean_before_and_after, multiple_forge_projects):

def test_get_by_forge_namespace(clean_before_and_after, multiple_copr_builds):
projects = list(
GitProjectModel.get_by_forge_namespace("github.com", "the-namespace")
GitProjectModel.get_by_forge_namespace(0, 10, "github.com", "the-namespace")
)
assert projects[0].namespace == "the-namespace"
assert projects[0].repo_name == "the-repo-name"
Expand Down Expand Up @@ -716,7 +716,7 @@ def test_get_project_prs(clean_before_and_after, a_copr_build_for_pr):
def test_get_project_branch(clean_before_and_after, a_copr_build_for_branch_push):
branches_list = list(
GitProjectModel.get_project_branches(
"github.com", "the-namespace", "the-repo-name"
0, 10, "github.com", "the-namespace", "the-repo-name"
)
)
assert len(branches_list) == 1
Expand All @@ -726,7 +726,7 @@ def test_get_project_branch(clean_before_and_after, a_copr_build_for_branch_push
def test_get_project_issues(clean_before_and_after, an_issue_model):
issues_list = list(
GitProjectModel.get_project_issues(
"github.com", "the-namespace", "the-repo-name"
0, 10, "github.com", "the-namespace", "the-repo-name"
)
)
assert len(issues_list) == 1
Expand All @@ -736,7 +736,7 @@ def test_get_project_issues(clean_before_and_after, an_issue_model):
def test_get_project_releases(clean_before_and_after, release_model):
releases = list(
GitProjectModel.get_project_releases(
"github.com", "the-namespace", "the-repo-name"
0, 10, "github.com", "the-namespace", "the-repo-name"
)
)
assert releases[0].tag_name == "v1.0.2"
Expand Down

0 comments on commit 9644107

Please sign in to comment.