Skip to content

Commit

Permalink
Add API endpoint for Bodhi updates (#2310)
Browse files Browse the repository at this point in the history
Add API endpoint for Bodhi updates

Fixes #1891

RELEASE NOTES BEGIN
There is a new API endpoint /bodhi-updates for getting information about Bodhi updates submitted by Packit.
RELEASE NOTES END

Reviewed-by: Maja Massarini
  • Loading branch information
softwarefactory-project-zuul[bot] authored Jan 19, 2024
2 parents 41f7734 + 4245a48 commit d7fc99c
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 0 deletions.
17 changes: 17 additions & 0 deletions packit_service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1856,6 +1856,23 @@ def create(

return update

@classmethod
def get_by_id(cls, id_: int) -> Optional["BodhiUpdateTargetModel"]:
return sa_session().query(BodhiUpdateTargetModel).filter_by(id=id_).first()

@classmethod
def get_all(cls) -> Iterable["BodhiUpdateTargetModel"]:
return sa_session().query(BodhiUpdateTargetModel)

@classmethod
def get_range(cls, first: int, last: int) -> Iterable["BodhiUpdateTargetModel"]:
return (
sa_session()
.query(BodhiUpdateTargetModel)
.order_by(desc(BodhiUpdateTargetModel.id))
.slice(first, last)
)


class BodhiUpdateGroupModel(ProjectAndEventsConnector, GroupModel, Base):
__tablename__ = "bodhi_update_groups"
Expand Down
2 changes: 2 additions & 0 deletions packit_service/service/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from packit_service.service.api.usage import usage_ns
from packit_service.service.api.pull_from_upstream import ns as pull_from_upstream_ns
from packit_service.service.api.system import ns as system_ns
from packit_service.service.api.bodhi_updates import ns as bodhi_updates_ns

# https://flask-restplus.readthedocs.io/en/stable/scaling.html
blueprint = Blueprint("api", __name__, url_prefix="/api")
Expand All @@ -42,3 +43,4 @@
api.add_namespace(usage_ns)
api.add_namespace(pull_from_upstream_ns)
api.add_namespace(system_ns)
api.add_namespace(bodhi_updates_ns)
113 changes: 113 additions & 0 deletions packit_service/service/api/bodhi_updates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from http import HTTPStatus
from logging import getLogger

from flask_restx import Namespace, Resource

from packit_service.models import (
optional_timestamp,
BodhiUpdateTargetModel,
BodhiUpdateGroupModel,
)
from packit_service.service.api.parsers import indices, pagination_arguments
from packit_service.service.api.utils import get_project_info_from_build, response_maker

logger = getLogger("packit_service")

ns = Namespace("bodhi-updates", description="Bodhi updates")


@ns.route("")
class BodhiUpdatesList(Resource):
@ns.expect(pagination_arguments)
@ns.response(HTTPStatus.PARTIAL_CONTENT, "Bodhi updates list follows")
def get(self):
"""List all Bodhi updates."""
first, last = indices()
result = []

for update in BodhiUpdateTargetModel.get_range(first, last):
update_dict = {
"packit_id": update.id,
"status": update.status,
"branch": update.target,
"web_url": update.web_url,
"koji_nvr": update.koji_nvr,
"alias": update.alias,
"pr_id": update.get_pr_id(),
"branch_name": update.get_branch_name(),
"release": update.get_release_tag(),
}

if project := update.get_project():
update_dict["project_url"] = project.project_url
update_dict["repo_namespace"] = project.namespace
update_dict["repo_name"] = project.repo_name

result.append(update_dict)

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


@ns.route("/<int:id>")
@ns.param("id", "Packit id of the update")
class BodhiUpdateItem(Resource):
@ns.response(HTTPStatus.OK, "OK, Bodhi update details follow")
@ns.response(HTTPStatus.NOT_FOUND.value, "No info about Bodhi update stored in DB")
def get(self, id):
"""A specific Bodhi updates details."""
update = BodhiUpdateTargetModel.get_by_id(int(id))

if not update:
return response_maker(
{"error": "No info about update stored in DB"},
status=HTTPStatus.NOT_FOUND,
)

update_dict = {
"status": update.status,
"branch": update.target,
"web_url": update.web_url,
"koji_nvr": update.koji_nvr,
"alias": update.alias,
"run_ids": sorted(run.id for run in update.group_of_targets.runs),
}

update_dict.update(get_project_info_from_build(update))
return response_maker(update_dict)


@ns.route("/groups/<int:id>")
@ns.param("id", "Packit id of the Bodhi update group")
class BodhiUpdateGroup(Resource):
@ns.response(HTTPStatus.OK, "OK, Bodhi update group details follow")
@ns.response(
HTTPStatus.NOT_FOUND.value, "No info about Bodhi update group stored in DB"
)
def get(self, id):
"""A specific Bodhi update group details."""
group_model = BodhiUpdateGroupModel.get_by_id(int(id))

if not group_model:
return response_maker(
{"error": "No info about group stored in DB"},
status=HTTPStatus.NOT_FOUND,
)

group_dict = {
"submitted_time": optional_timestamp(group_model.submitted_time),
"run_ids": sorted(run.id for run in group_model.runs),
"update_target_ids": sorted(
build.id for build in group_model.grouped_targets
),
}

group_dict.update(get_project_info_from_build(group_model))
return response_maker(group_dict)
2 changes: 2 additions & 0 deletions packit_service/service/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
SyncReleaseModel,
SyncReleaseTargetModel,
optional_timestamp,
BodhiUpdateTargetModel,
)


Expand All @@ -37,6 +38,7 @@ def get_project_info_from_build(
TFTTestRunTargetModel,
TFTTestRunGroupModel,
SyncReleaseModel,
BodhiUpdateTargetModel,
]
) -> Dict[str, Any]:
if not (project := build.get_project()):
Expand Down
48 changes: 48 additions & 0 deletions tests_openshift/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
SourceGitPRDistGitPRModel,
BuildStatus,
SyncReleaseJobType,
BodhiUpdateTargetModel,
BodhiUpdateGroupModel,
)
from packit_service.worker.events import InstallationEvent

Expand Down Expand Up @@ -138,6 +140,14 @@ class SampleValues:
},
]

# dist-git
nvr = "packit-0.43.0-1.fc39"
dist_git_branch = "f39"
different_nvr = "packit-0.40.0-1.fc38"
different_dist_git_branch = "f38"
alias = "FEDORA-123"
bodhi_url = "https://bodhi.fedoraproject.org/FEDORA-123"


@pytest.fixture(scope="session", autouse=True)
def global_service_config():
Expand Down Expand Up @@ -2295,3 +2305,41 @@ def source_git_dist_git_pr_new_relationship():
)

yield created


@pytest.fixture()
def bodhi_update_model(branch_project_event_model):
group = BodhiUpdateGroupModel.create(
run_model=PipelineModel.create(project_event=branch_project_event_model)
)
model = BodhiUpdateTargetModel.create(
target=SampleValues.dist_git_branch,
koji_nvr=SampleValues.nvr,
status="queued",
bodhi_update_group=group,
)
model.set_alias(SampleValues.alias)
model.set_web_url(SampleValues.bodhi_url)
model.set_status("error")
yield model


@pytest.fixture()
def multiple_bodhi_update_runs(branch_project_event_model):
group = BodhiUpdateGroupModel.create(
run_model=PipelineModel.create(project_event=branch_project_event_model)
)
yield [
BodhiUpdateTargetModel.create(
target=SampleValues.dist_git_branch,
koji_nvr=SampleValues.nvr,
status="queued",
bodhi_update_group=group,
),
BodhiUpdateTargetModel.create(
target=SampleValues.different_dist_git_branch,
koji_nvr=SampleValues.different_nvr,
status="queued",
bodhi_update_group=group,
),
]
43 changes: 43 additions & 0 deletions tests_openshift/service/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,3 +826,46 @@ def test_project_usage_info(
)
== 1
)


def test_bodhi_update_list(
client,
clean_before_and_after,
multiple_bodhi_update_runs,
):
response = client.get(url_for("api.bodhi-updates_bodhi_updates_list"))
response_dict = response.json

assert len(response_dict) == 2

response_dict.reverse()
assert response_dict[0]["status"] == "queued"
assert response_dict[0]["koji_nvr"] == SampleValues.nvr
assert response_dict[0]["branch"] == SampleValues.dist_git_branch
assert response_dict[0]["branch_name"] == SampleValues.branch

assert response_dict[1]["koji_nvr"] == SampleValues.different_nvr
assert response_dict[1]["branch"] == SampleValues.different_dist_git_branch

assert response_dict[0]["repo_namespace"] == SampleValues.repo_namespace
assert response_dict[0]["repo_name"] == SampleValues.repo_name
assert response_dict[0]["project_url"] == SampleValues.project_url


def test_bodhi_update_info(
client,
clean_before_and_after,
bodhi_update_model,
):
response = client.get(
url_for("api.bodhi-updates_bodhi_update_item", id=bodhi_update_model.id)
)
response_dict = response.json
assert response_dict["alias"] == SampleValues.alias
assert response_dict["branch"] == SampleValues.dist_git_branch
assert response_dict["web_url"] == SampleValues.bodhi_url
assert response_dict["koji_nvr"] == SampleValues.nvr
assert response_dict["branch_name"] == SampleValues.branch
assert response_dict["repo_name"] == SampleValues.repo_name
assert response_dict["repo_namespace"] == SampleValues.repo_namespace
assert response_dict["status"] == "error"

0 comments on commit d7fc99c

Please sign in to comment.