Skip to content

Commit

Permalink
frontend: migrate build-chroots to flask-restx
Browse files Browse the repository at this point in the history
  • Loading branch information
nikromen authored and praiskup committed Feb 2, 2024
1 parent 5a881ad commit dba3811
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 66 deletions.
152 changes: 111 additions & 41 deletions frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_build_chroots.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import flask
from coprs.views.apiv3_ns import apiv3_ns
# pylint: disable=missing-class-docstring

from http import HTTPStatus

from flask_restx import Namespace, Resource

from coprs.views.apiv3_ns import api, query_to_parameters
from coprs import models
from coprs.logic.builds_logic import BuildChrootsLogic
from coprs.logic.coprs_logic import CoprChrootsLogic
from coprs.logic.complex_logic import BuildConfigLogic, ComplexLogic
from . import query_params, pagination, Paginator, GET
from coprs.views.apiv3_ns.schema.schemas import (
build_chroot_model,
build_chroot_params,
build_id_params,
pagination_params,
pagination_build_chroot_model,
build_chroot_config_model,
nevra_packages_model,
)
from coprs.views.apiv3_ns import restx_pagination
from . import Paginator


apiv3_bchroots_ns = Namespace("build-chroot", description="Build Chroots")
api.add_namespace(apiv3_bchroots_ns)


def to_dict(build_chroot):
Expand Down Expand Up @@ -36,41 +55,92 @@ def build_config(build_chroot):
return dict_data


@apiv3_ns.route("/build-chroot", methods=GET)
@apiv3_ns.route("/build-chroot/<int:build_id>/<chrootname>", methods=GET) # deprecated
@query_params()
def get_build_chroot(build_id, chrootname):
chroot = ComplexLogic.get_build_chroot(build_id, chrootname)
return flask.jsonify(to_dict(chroot))


@apiv3_ns.route("/build-chroot/list", methods=GET)
@apiv3_ns.route("/build-chroot/list/<int:build_id>", methods=GET)
@pagination()
@query_params()
def get_build_chroot_list(build_id, **kwargs):
# For the python3-copr <= 1.105
if kwargs.get("order") == "name":
kwargs.pop("order")
query = BuildChrootsLogic.filter_by_build_id(BuildChrootsLogic.get_multiply(), build_id)
paginator = Paginator(query, models.BuildChroot, **kwargs)
chroots = paginator.map(to_dict)
return flask.jsonify(items=chroots, meta=paginator.meta)


@apiv3_ns.route("/build-chroot/build-config", methods=GET)
@apiv3_ns.route("/build-chroot/build-config/<int:build_id>/<chrootname>", methods=GET) # deprecated
@query_params()
def get_build_chroot_config(build_id, chrootname):
chroot = ComplexLogic.get_build_chroot(build_id, chrootname)
return flask.jsonify(build_config(chroot))


@apiv3_ns.route("/build-chroot/built-packages/", methods=GET)
@query_params()
def get_build_chroot_built_packages(build_id, chrootname):
"""
Return built packages (NEVRA dicts) for a given build chroot
"""
chroot = ComplexLogic.get_build_chroot(build_id, chrootname)
return flask.jsonify(chroot.results_dict)
@apiv3_bchroots_ns.route("/")
@apiv3_bchroots_ns.route(
"/<int:build_id>/<chrootname>",
doc={"deprecated": True, "description": "Use query parameters instead"},
)
class BuildChroot(Resource):
@query_to_parameters
@apiv3_bchroots_ns.doc(params=build_chroot_params)
@apiv3_bchroots_ns.marshal_with(build_chroot_model)
@apiv3_bchroots_ns.response(HTTPStatus.OK.value, "OK, Build chroot data follows...")
@apiv3_bchroots_ns.response(
HTTPStatus.NOT_FOUND.value, "No such Build chroot exist"
)
def get(self, build_id, chrootname):
"""
Get build chroot
Get information about specific build chroot by build id and mock chroot name.
"""
chroot = ComplexLogic.get_build_chroot(build_id, chrootname)
return to_dict(chroot)


@apiv3_bchroots_ns.route("/list")
@apiv3_bchroots_ns.route(
"/list/<int:build_id>",
doc={"deprecated": True, "description": "Use query parameters instead"},
)
class BuildChrootList(Resource):
@restx_pagination
@query_to_parameters
@apiv3_bchroots_ns.doc(params=build_id_params | pagination_params)
@apiv3_bchroots_ns.marshal_list_with(pagination_build_chroot_model)
@apiv3_bchroots_ns.response(
HTTPStatus.PARTIAL_CONTENT.value, HTTPStatus.PARTIAL_CONTENT.description
)
def get(self, build_id, **kwargs):
"""
List Build chroots
List build chroots by build id and pagination query.
"""
# For the python3-copr <= 1.105
if kwargs.get("order") == "name":
kwargs.pop("order")
query = BuildChrootsLogic.filter_by_build_id(BuildChrootsLogic.get_multiply(), build_id)
paginator = Paginator(query, models.BuildChroot, **kwargs)
chroots = paginator.map(to_dict)
return {"items": chroots, "meta": paginator.meta}


@apiv3_bchroots_ns.route("/build-config")
@apiv3_bchroots_ns.route(
"/build-config/<int:build_id>/<chrootname>",
doc={"deprecated": True, "description": "Use query parameters instead"},
)
class BuildChrootConfig(Resource):
@query_to_parameters
@apiv3_bchroots_ns.doc(params=build_chroot_params)
@apiv3_bchroots_ns.marshal_with(build_chroot_config_model, skip_none=True)
@apiv3_bchroots_ns.response(HTTPStatus.OK.value, "OK, Build chroot config follows...")
@apiv3_bchroots_ns.response(
HTTPStatus.NOT_FOUND.value, "No such Build chroot exist"
)
def get(self, build_id, chrootname):
"""
Get Build chroot config
Get Build chroot by build id and its mock chroot name.
"""
chroot = ComplexLogic.get_build_chroot(build_id, chrootname)
return build_config(chroot)


@apiv3_bchroots_ns.route("/built-packages")
class BuildChrootPackages(Resource):
@query_to_parameters
@apiv3_bchroots_ns.doc(params=build_chroot_params)
@apiv3_bchroots_ns.marshal_with(nevra_packages_model)
@apiv3_bchroots_ns.response(
HTTPStatus.OK.value, "OK, dict containing all built packages in this chroot follows..."
)
@apiv3_bchroots_ns.response(
HTTPStatus.NOT_FOUND.value, "No such Build chroot exist"
)
def get(self, build_id, chrootname):
"""
Get built packages
Get built packages (NEVRA dicts) for a given mock chroot name.
"""
chroot = ComplexLogic.get_build_chroot(build_id, chrootname)
return chroot.results_dict
47 changes: 40 additions & 7 deletions frontend/coprs_frontend/coprs/views/apiv3_ns/schema/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""


from flask_restx.fields import String, List, Integer, Boolean, Url, Raw
from flask_restx.fields import String, List, Integer, Boolean, Raw, StringMixin

# TODO: split these fields to some hierarchy e.g. using dataclasses or to some clusters

Expand All @@ -18,6 +18,27 @@
# TODO: this file is not perfect in documenting... missing enums, choices, etc.


class Url(StringMixin, Raw):
"""
Feel free to drop this if you want to spend 4 hours why the fuck everything
stopped working. TLDR; query_to_parameters and flask_restx.fields.Url.output
aren't friends.
Flask_restx is opinionated and tries to follow some conventions, we break one of
them by query_to_parameters decorator. The problem begins when Url field is used
in marshaling because flask_restx tries to be clever and it is building example
URL for you to documentation page as output. And to be even more clever, if no
URL from user was provided, it uses flask.request.endpoint route and tries to
build endpoint with correct values. Remember we tinker with the values in
query_to_parameters so it screws.
"""
__schema_format__ = "uri"

def __init__(self, example=None, **kwargs):
self.example = example or "https://www.example.uwu/xyz"
super().__init__(example=example, **kwargs)


id_field = Integer(
description="Numeric ID",
example=123,
Expand Down Expand Up @@ -170,7 +191,7 @@
description="Auto-rebuild the package? (i.e. every commit or new tag)",
)

clone_url = String(
clone_url = Url(
description="URL to your Git or SVN repository",
example="https://github.com/fedora-copr/copr.git",
)
Expand All @@ -196,8 +217,6 @@
example=["fedora-37-x86_64", "fedora-rawhide-x86_64"],
)

chroots.clone()

submitted_on = Integer(
description="Timestamp when the build was submitted",
example=1677695304,
Expand Down Expand Up @@ -414,6 +433,20 @@
)
)

timeout = Integer(
default=18000,
example=123123,
description="Number of seconds we allow the builds to run.",
)

release = String(example="1.fc39")

epoch = Integer(example=3)

arch = String(example="x86_64")

version = String(example="1.0")

# TODO: these needs description

chroot_repos = Raw()
Expand All @@ -424,13 +457,13 @@

priority = Integer()

memory_limit = Integer()

# TODO: specify those only in Repo schema?

baseurl = Url()

url = String()

version = String()
url = Url()

webhook_rebuild = Boolean()

Expand Down
89 changes: 71 additions & 18 deletions frontend/coprs_frontend/coprs/views/apiv3_ns/schema/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,21 @@


from dataclasses import dataclass, fields, asdict, MISSING
from functools import wraps
from typing import Any

from flask_restx.fields import String, List, Integer, Boolean, Nested, Url, Raw
from flask_restx.fields import String, List, Integer, Boolean, Nested, Raw

from coprs.views.apiv3_ns import api
from coprs.views.apiv3_ns.schema import fields as schema_fields
from coprs.views.apiv3_ns.schema.fields import scm_type, mock_chroot, additional_repos, clone
from coprs.views.apiv3_ns.schema.fields import (
scm_type,
mock_chroot,
additional_repos,
clone,
id_field,
url,
Url,
)


@dataclass
Expand Down Expand Up @@ -221,20 +228,6 @@ class PaginationMeta(ParamsSchema):
_pagination_meta_model = PaginationMeta.get_cls().model()


# checks if `items` in Pagination schema are defined (don't have None value)
def _check_if_items_are_defined(method):
@wraps(method)
def check_items(self, *args, **kwargs):
if getattr(self, "items") is None:
raise KeyError(
"No items are defined in Pagination. Perhaps you forgot to"
" specify it when creating Pagination instance?"
)
return method(self, *args, **kwargs)

return check_items


@dataclass
class Pagination(Schema):
"""
Expand All @@ -245,8 +238,13 @@ class Pagination(Schema):
items: Any = None
meta: Nested = Nested(_pagination_meta_model)

@_check_if_items_are_defined
def model(self):
if self.items is None:
raise KeyError(
"No items are defined in Pagination. Perhaps you forgot to"
" specify it when creating Pagination instance?"
)

return super().model()


Expand Down Expand Up @@ -486,15 +484,68 @@ class ProjectParamsSchema(ParamsSchema):
exist_ok: Boolean


@dataclass
class BuildChroot(Schema):
started_on: Integer
ended_on: Integer
state: String
name: String = mock_chroot
result_url: Url = url


@dataclass
class BuildChrootParams(ParamsSchema):
build_id: Integer = id_field
chrootname: String = mock_chroot

__all_required: bool = True


@dataclass
class BuildChrootConfig(Schema):
additional_repos: List
additional_packages: List
with_opts: List
without_opts: List
enable_net: Boolean
is_background: Boolean
memory_limit: Integer
timeout: Integer
bootstrap: String
bootstrap_image: String
repos: List = List(Nested(_repo_model))


@dataclass
class Nevra(Schema):
arch: String
epoch: Integer
release: String
version: String
name: String = String(description="Package name")


_nevra_model = Nevra.get_cls().model()


@dataclass
class NevraPackages(Schema):
packages: List = List(Nested(_nevra_model))


# OUTPUT MODELS
project_chroot_model = ProjectChroot.get_cls().model()
project_chroot_build_config_model = ProjectChrootBuildConfig.get_cls().model()
source_dict_scm_model = SourceDictScm.get_cls().model()
source_dict_pypi_model = SourceDictPyPI.get_cls().model()
package_model = Package.get_cls().model()
project_model = Project.get_cls().model()
build_chroot_model = BuildChroot.get_cls().model()
build_chroot_config_model = BuildChrootConfig.get_cls().model()
nevra_packages_model = NevraPackages.get_cls().model()

pagination_project_model = Pagination(items=List(Nested(project_model))).model()
pagination_build_chroot_model = Pagination(items=List(Nested(build_chroot_model))).model()

source_package_model = _source_package_model
build_model = _build_model
Expand All @@ -518,3 +569,5 @@ class ProjectParamsSchema(ParamsSchema):
fullname_params = FullnameSchema.get_cls().params_schema()
project_params = ProjectParamsSchema.get_cls().params_schema()
pagination_params = PaginationMeta.get_cls().params_schema()
build_chroot_params = BuildChrootParams.get_cls().params_schema()
build_id_params = {"build_id": build_chroot_params["build_id"]}

0 comments on commit dba3811

Please sign in to comment.