diff --git a/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py b/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py index 4e47bdf37..1cbef99f2 100644 --- a/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py +++ b/frontend/coprs_frontend/coprs/views/apiv3_ns/apiv3_packages.py @@ -29,10 +29,11 @@ package_get_params, package_add_input_model, package_edit_input_model, + package_reset_input_model, + package_delete_input_model, package_get_list_params, pagination_package_model, build_model, - base_package_input_model, ) from coprs.views.apiv3_ns.schema.docs import add_package_docs, edit_package_docs from coprs.logic.packages_logic import PackagesLogic @@ -247,7 +248,7 @@ def _common(): @api_login_required @apiv3_packages_ns.marshal_with(package_model) - @apiv3_packages_ns.expect(base_package_input_model) + @apiv3_packages_ns.expect(package_reset_input_model) def put(self): """ Reset a package @@ -258,7 +259,7 @@ def put(self): @deprecated_route_method_type(apiv3_packages_ns, "POST", "PUT") @api_login_required @apiv3_packages_ns.marshal_with(package_model) - @apiv3_packages_ns.expect(base_package_input_model) + @apiv3_packages_ns.expect(package_reset_input_model) def post(self): """ Reset a package @@ -323,7 +324,7 @@ def _common(): @api_login_required @apiv3_packages_ns.marshal_with(package_model) - @apiv3_packages_ns.expect(base_package_input_model) + @apiv3_packages_ns.expect(package_delete_input_model) def delete(self): """ Delete a package @@ -334,7 +335,7 @@ def delete(self): @deprecated_route_method_type(apiv3_packages_ns, "POST", "DELETE") @api_login_required @apiv3_packages_ns.marshal_with(package_model) - @apiv3_packages_ns.expect(base_package_input_model) + @apiv3_packages_ns.expect(package_delete_input_model) def post(self): """ Delete a package diff --git a/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/fields.py b/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/fields.py index 1cea8c5e5..14739820a 100644 --- a/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/fields.py +++ b/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/fields.py @@ -42,21 +42,25 @@ def __init__(self, example=None, **kwargs): id_field = Integer( description="Numeric ID", example=123, + readonly=True, ) mock_chroot = String( description="Mock chroot", example="fedora-rawhide-x86_64", + # required=True, ) ownername = String( description="User or group name", example="@copr", + readonly=True, ) full_name = String( description="Full name of the project", example="@copr/pull-requests", + readonly=True, ) projectname = String( diff --git a/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/schemas.py b/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/schemas.py index cbb68bfd2..120e2fb9f 100644 --- a/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/schemas.py +++ b/frontend/coprs_frontend/coprs/views/apiv3_ns/schema/schemas.py @@ -150,7 +150,8 @@ def params_schema(self) -> dict: def _field_schema(self, field): attr = getattr(self, field.name) schema = {k: v for k, v in attr.schema().items() if v is not None} - if attr in self.required_attrs: + + if attr in self.required_attrs or attr.required: schema |= {"required": True} return schema @@ -187,26 +188,18 @@ def model(self): @dataclass -class _ProjectChrootFields: - additional_repos: List = fields.additional_repos - additional_packages: List = fields.additional_packages - additional_modules: List = fields.additional_modules - with_opts: List = fields.with_opts - without_opts: List = fields.without_opts - isolation: String = fields.isolation +class Repo(Schema): + baseurl: Url = Url() + module_hotfixes: Boolean = fields.module_hotfixes + priority: Integer = Integer( + description="The priority value of this repository. Defaults to 99", + example=42, + ) + id: String = String(example="copr_base") + name: String = String(example="Copr repository") -@dataclass -class ProjectChroot(_ProjectChrootFields, Schema): - mock_chroot: String = fields.mock_chroot - ownername: String = fields.ownername - projectname: String = fields.projectname - comps_name: String = String(description="Name of the comps.xml file") - delete_after_days: Integer = Integer( - description=("The project will be automatically deleted after " - "this many days"), - example=30, - ) +_repo_model = Repo.get_cls().model() @dataclass @@ -221,29 +214,53 @@ def required_attrs(self) -> list: @dataclass -class Repo(Schema): - baseurl: Url = Url() - module_hotfixes: Boolean = fields.module_hotfixes - priority: Integer = Integer( - description="The priority value of this repository. Defaults to 99", - example=42, +class ProjectChroot(Schema): + mock_chroot: String = fields.mock_chroot + ownername: String = fields.ownername + projectname: String = fields.projectname + comps_name: String = String(description="Name of the comps.xml file") + delete_after_days: Integer = Integer( + description=("The project will be automatically deleted after " + "this many days"), + example=30, ) - id: String = String(example="copr_base") - name: String = String(example="Copr repository") - - -_repo_model = Repo.get_cls().model() + additional_repos: List = fields.additional_repos + additional_packages: List = fields.additional_packages + additional_modules: List = fields.additional_modules + with_opts: List = fields.with_opts + without_opts: List = fields.without_opts + isolation: String = fields.isolation @dataclass -class ProjectChrootBuildConfig(_ProjectChrootFields, Schema): +class ProjectChrootBuildConfig(Schema): chroot: String = fields.chroot enable_net: Boolean = fields.enable_net repos: List = List(Nested(_repo_model)) + additional_repos: List = fields.additional_repos + additional_packages: List = fields.additional_packages + additional_modules: List = fields.additional_modules + with_opts: List = fields.with_opts + without_opts: List = fields.without_opts + isolation: String = fields.isolation @dataclass -class _SourceDictScmFields: +class SourceDictUrl: + pkgs: List = List( + Url, + description="List of urls to build from", + example=["https://example.com/some.src.rpm"], + ) + + +@dataclass +class SourceDictUpload: + pkgs: List = List(Raw, description="application/x-rpm files to build from") + + +@dataclass +class SourceDictSCM: clone_url: String = Url( description="URL to your Git or SVN repository", example="https://github.com/fedora-copr/copr.git", @@ -257,16 +274,12 @@ class _SourceDictScmFields: description="Subdirectory where source files and .spec are located", example="cli", ) - - -@dataclass -class SourceDictScm(_SourceDictScmFields, Schema): + scm_type: String = fields.scm_type source_build_method: String = fields.source_build_method - type: String = scm_type @dataclass -class SourceDictPyPI(Schema): +class SourceDictPyPI: pypi_package_name: String = String( description="Package name in the Python Package Index.", example="copr", @@ -303,6 +316,31 @@ class SourceDictPyPI(Schema): ) +@dataclass +class SourceDictDistGit: + distgit: String = fields.distgit + namespace: String = String( + description="DistGit namescape", + example="@copr/copr", + ) + packagename: String = fields.packagename + committish: String = fields.committish + + +@dataclass +class SourceDictRubyGems: + gem_name: String = fields.gem_name + + +@dataclass +class SourceDictCustom: + script: String = fields.script + chroot: String = fields.chroot + builddeps: String = fields.builddeps + resultdir: String = fields.resultdir + repos: List = List(Nested(_repo_model)) + + @dataclass class SourcePackage(Schema): name: String = fields.name @@ -389,7 +427,21 @@ def required_attrs(self) -> list: @dataclass -class BasePackage(InputSchema): +class PackageAdd(SourceDictSCM, SourceDictPyPI, InputSchema): + # rest of SCM + scm_type: String = fields.scm_type + + # Rubygems + gem_name: String = fields.gem_name + + # Custom + script: String = fields.script + builddeps: String = fields.builddeps + resultdir: String = fields.resultdir + chroot: String = fields.chroot + + source_build_method: String = fields.source_build_method + max_builds: Integer = Integer( description=( "Keep only the specified number of the newest-by-id builds " @@ -399,28 +451,33 @@ class BasePackage(InputSchema): ) timeout: Integer = fields.timeout webhook_rebuild: Boolean = Boolean() + # FIXME should this be package_name? packagename: String = fields.packagename @dataclass -class PackageAdd(_SourceDictScmFields, SourceDictPyPI, BasePackage, InputSchema): - # rest of SCM - scm_type: String = fields.scm_type - - # Rubygems - gem_name: String = fields.gem_name +class PackageReset(InputSchema): + ownername: String = fields.ownername + projectname: String = fields.projectname + # FIXME should this be package_name? + packagename: String = fields.packagename - # Custom - script: String = fields.script - builddeps: String = fields.builddeps - resultdir: String = fields.resultdir - chroot: String = fields.chroot - source_build_method: String = fields.source_build_method +@dataclass +class PackageDelete(InputSchema): + ownername: String = fields.ownername + projectname: String = fields.projectname + # FIXME should this be package_name? + packagename: String = fields.packagename @dataclass -class _ProjectFields: +class Project(Schema): + id: Integer = fields.id_field + ownername: String = fields.ownername + full_name: String = fields.full_name + chroot_repos: Raw = Raw(readonly=True) + homepage: Url = Url( description="Homepage URL of Copr project", example="https://github.com/fedora-copr", @@ -469,52 +526,25 @@ class _ProjectFields: example=42, ) - -@dataclass -class _ProjectGetAddFields: name: String = fields.name persistent: Boolean = Boolean( description="Build and project is immune against deletion", ) - additional_repos: List = fields.additional_repos - -@dataclass -class Project(_ProjectFields, _ProjectGetAddFields, Schema): - id: Integer = fields.id_field - ownername: String = fields.ownername - full_name: String = fields.full_name - chroot_repos: Raw = Raw() - - -@dataclass -class _ProjectAddEditFields: - chroots: List = fields.chroots - bootstrap_image: String = fields.bootstrap_image - multilib: Boolean = Boolean() - fedora_review: Boolean = Boolean( - description="Run fedora-review tool for packages in this project" - ) - runtime_dependencies: String = String( - description=( - "List of external repositories (== dependencies, specified as " - "baseurls) that will be automatically enabled together with " - "this project repository." - ) - ) + # FIXME repos + additional_repos: List = fields.additional_repos + # TODO: fix inconsistency - additional_repos <-> repos + # repos: String = additional_repos @dataclass -class ProjectAdd( - _ProjectFields, _ProjectGetAddFields, _ProjectAddEditFields, InputSchema -): +class ProjectAdd(Project, InputSchema): ... @dataclass -class ProjectEdit(_ProjectFields, _ProjectAddEditFields, InputSchema): - # TODO: fix inconsistency - additional_repos <-> repos - repos: String = additional_repos +class ProjectEdit(Project, InputSchema): + ... @dataclass @@ -531,6 +561,7 @@ class ProjectFork(InputSchema): @dataclass class ProjectDelete(InputSchema): + # FIXME missing ownername and projectname? verify: Boolean = Boolean() @@ -651,7 +682,7 @@ class ModuleAdd(InputSchema): @dataclass -class _ModulePackage(Schema): +class MonitorPackage(Schema): name: String = fields.name # inconsistent keys in chroots dict, impossible with flask-restx to do chroots: Raw = Raw( @@ -660,14 +691,14 @@ class _ModulePackage(Schema): ) -_module_package_model = _ModulePackage.get_cls().model() +monitor_package_model = MonitorPackage.get_cls().model() @dataclass class Monitor(Schema): message: String = String(example="Project monitor request successful") output: String = String(example="ok") - packages: List = List(Nested(_module_package_model)) + packages: List = List(Nested(monitor_package_model)) @dataclass @@ -698,7 +729,10 @@ def required_attrs(self) -> list: @dataclass -class _GenericBuildOptions: +class BaseBuild: + ownername: String = fields.ownername + projectname: String = fields.projectname + project_dirname: String = fields.project_dirname chroot_names: List = List( String, description="List of chroot names", @@ -729,65 +763,38 @@ class _GenericBuildOptions: @dataclass -class _BuildDataCommon: - ownername: String = fields.ownername - projectname: String = fields.projectname - - -@dataclass -class CreateBuildUrl(_BuildDataCommon, _GenericBuildOptions, InputSchema): - project_dirname: String = fields.project_dirname - pkgs: List = List( - Url, - description="List of urls to build from", - example=["https://example.com/some.src.rpm"], - ) +class CreateBuildUrl(SourceDictUrl, BaseBuild, InputSchema): + ... @dataclass -class CreateBuildUpload(_BuildDataCommon, _GenericBuildOptions, InputSchema): - project_dirname: String = fields.project_dirname - pkgs: List = List(Raw, description="application/x-rpm files to build from") +class CreateBuildUpload(SourceDictUpload, BaseBuild, InputSchema): + ... @dataclass -class CreateBuildSCM(_BuildDataCommon, _GenericBuildOptions, _SourceDictScmFields, InputSchema): - project_dirname: String = fields.project_dirname - scm_type: String = fields.scm_type - source_build_method: String = fields.source_build_method +class CreateBuildSCM(SourceDictSCM, BaseBuild, InputSchema): + ... @dataclass -class CreateBuildDistGit(_BuildDataCommon, _GenericBuildOptions, InputSchema): - distgit: String = fields.distgit - namespace: String = String( - description="DistGit namescape", - example="@copr/copr", - ) - package_name: String = fields.packagename - committish: String = fields.committish - project_dirname: String = fields.project_dirname +class CreateBuildDistGit(SourceDictDistGit, BaseBuild, InputSchema): + ... @dataclass -class CreateBuildPyPI(_BuildDataCommon, _GenericBuildOptions, SourceDictPyPI, InputSchema): - project_dirname: String = fields.project_dirname +class CreateBuildPyPI(SourceDictPyPI, BaseBuild, InputSchema): + ... @dataclass -class CreateBuildRubyGems(_BuildDataCommon, _GenericBuildOptions, InputSchema): - project_dirname: String = fields.project_dirname - gem_name: String = fields.gem_name +class CreateBuildRubyGems(SourceDictRubyGems, BaseBuild, InputSchema): + ... @dataclass -class CreateBuildCustom(_BuildDataCommon, _GenericBuildOptions, InputSchema): - script: String = fields.script - chroot: String = fields.chroot - builddeps: String = fields.builddeps - resultdir: String = fields.resultdir - project_dirname: String = fields.project_dirname - repos: List = List(Nested(_repo_model)) +class CreateBuildCustom(SourceDictCustom, BaseBuild, InputSchema): + ... @dataclass @@ -795,12 +802,9 @@ class DeleteBuilds(InputSchema): builds: List = List(Integer, description="List of build ids to delete") - # 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() @@ -828,7 +832,8 @@ class DeleteBuilds(InputSchema): # INPUT MODELS package_add_input_model = PackageAdd.get_cls().input_model() package_edit_input_model = package_add_input_model -base_package_input_model = BasePackage.get_cls().input_model() +package_reset_input_model = PackageReset.get_cls().input_model() +package_delete_input_model = PackageDelete.get_cls().input_model() project_add_input_model = ProjectAdd.get_cls().input_model() project_edit_input_model = ProjectEdit.get_cls().input_model()