#1770 frontend: create project for Fedora Review
Merged 3 years ago by frostyx. Opened 3 years ago by praiskup.
Unknown source fedora-review-form  into  main

@@ -287,6 +287,33 @@

              raise wtforms.ValidationError(self.message.format(field.data))

  

  

+ class CoprUniqueNameValidator2:

+     """

+     Validate that that Copr project name User gave us doesn't

+     cause duplicity.

+ 

+     This validator can only be used in CoprBaseForm descendants.

+ 

+     TODO: Replace all occurrences of CoprUniqueNameValidator with this.

+     """

+     usr_msg = "You already have"

+     grp_msg = "Group '{}' already has"

+ 

+     def __call__(self, form, field):

+         msg = self.usr_msg

+         if form.group:

+             msg = self.grp_msg.format(form.group.name)

+             existing = CoprsLogic.exists_for_group(

+                 form.group, field.data).first()

+         else:

+             existing = CoprsLogic.exists_for_user(

+                 form.user, field.data).first()

+ 

+         if existing:

+             msg = msg + " a project named \"{}\"".format(existing.name)

+             raise wtforms.ValidationError(msg)

+ 

+ 

  class NameCharactersValidator(object):

      def __init__(self, message=None):

          if not message:
@@ -424,6 +451,31 @@

          return value

  

  

+ class CoprBaseForm(FlaskForm):

+     """

+     All forms that modify Copr project should inherit from this.

+     """

+ 

+     def __init__(self, *args, user=None, group=None, **kwargs):

+         super().__init__(*args, **kwargs)

+         self.user = user

+         self.group = group

+ 

+ 

+ class CoprFedoraReviewForm(CoprBaseForm):

+     """

+     Simplified Copr form for FedoraReview-only project.

+     """

+     name = wtforms.StringField(

+         "Name",

+         validators=[

+             wtforms.validators.DataRequired(),

+             NameCharactersValidator(),

+             CoprUniqueNameValidator2(),

+             NameNotNumberValidator()

+         ])

+ 

+ 

  class CoprForm(FlaskForm):

      """

      Base form class for adding and modifying projects

@@ -0,0 +1,40 @@

+ {% extends "layout.html" %}

+ {% block title %}Add a Fedora Review Project{% endblock %}

+ {% block header %}Add a Fedora Review Project{% endblock %}

+ 

+ {% from "_helpers.html" import render_field %}

+ 

+ {% block breadcrumbs %}

+ <ol class="breadcrumb">

+   <li>

+     <a href="{{ url_for('coprs_ns.coprs_show') }}">Home</a>

+   </li>

+   <li>

+     <a href="{{ url_for('coprs_ns.coprs_by_user', username=g.user.name) }}">{{ g.user.name }}</a>

+   </li>

+   <li class="active">

+     New Fedora Review Project

+   </li>

+ </ol>

+ {% endblock %}

+ {% block body %}

+ 

+ <h1>Create a Fedora Review Project</h1>

+ <p>

+ You agree to build only <a href="https://docs.pagure.org/copr.copr/user_documentation.html#what-i-can-build-in-copr">allowed content</a> in Copr.

+ Check if your <a href="https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses">license</a> is allowed.

+ </p>

+ <form action="{{ request.path }}" method=post>

+   <div class="panel panel-default">

+     <div class="panel-heading">

+       <h3 class="panel-title">Project information</h3>

+     </div>

+     <div class="panel-body">

+       {{ form.csrf_token }}

+       {{ render_field(form.name) }}

+     </div>

+   </div>

+   <input class="btn btn-primary" type="submit" value="Create Fedora Review Project">

+ </form>

+ 

+ {% endblock %}

@@ -17,6 +17,9 @@

  

  {% block projects_header %}

  {% if g.user %}

+ <a href="{{url_for('coprs_ns.copr_add_fedora_review', username=g.user.name) }}"

+     class="btn button-new btn-secondary pull-right">

+   <span class="pficon pficon-add-circle-o"></span> Fedora Review </a>

  <a href="{{url_for('coprs_ns.copr_add', username=g.user.name) }}" class="btn button-new pull-right btn-primary">

    <span class="pficon pficon-add-circle-o"></span> New Project

  </a>

@@ -49,6 +49,10 @@

      <span class="pficon pficon-add-circle-o"></span> New Project

    </a>

  

+   <a href="{{url_for('coprs_ns.copr_add_fedora_review', username=g.user.name) }}"

+       class="btn button-new btn-secondary">

+     <span class="pficon pficon-add-circle-o"></span> Fedora Review </a>

+ 

    <a href="{{ url_for('user_ns.pinned_projects') }}" class="btn button-new btn-secondary">

      <span class="pficon pficon-edit"></span> Customize pinned

    </a>

@@ -154,6 +154,49 @@

                              graph=data)

  

  

+ @coprs_ns.route("/<username>/new-fedora-review/", methods=["GET", "POST"])

+ @login_required

+ def copr_add_fedora_review(username):  # pylint: disable=unused-argument

+     """ Show and process the simplified "Copr create" form for Fedora Review """

+     delete_after_days = 60

+ 

+     if username != flask.g.user.username:

+         # when accessed by accident

+         flask.flash("You can not add projects for '{}' user".format(username),

+                     "error")

+         return flask.redirect("/")

+ 

+     form = forms.CoprFedoraReviewForm(user=flask.g.user)

+     if flask.request.method == "POST":

+         if form.validate_on_submit():

+             copr = coprs_logic.CoprsLogic.add(

+                 flask.g.user,

+                 name=form.name.data,

+                 selected_chroots=["fedora-rawhide-x86_64"],

+                 description="Project was created only to execute Fedora Review "

+                             "tool for all builds.",

+                 instructions="You should ask the project owner before "

+                              "installing anything from this project.",

+                 unlisted_on_hp=True,

+                 delete_after_days=delete_after_days,

+                 follow_fedora_branching=False,

+                 fedora_review=True,

+             )

+             db.session.commit()

+             flask.flash(

+                 "New review project has been created successfully, will be removed "

+                 "after {} days (if not prolonged)".format(delete_after_days),

+                 "success")

+ 

+             return flask.redirect(

+                 helpers.copr_url('coprs_ns.copr_add_build', copr))

+ 

+         # validation problem

+         flask.flash("Error in project config", "error")

+ 

+     return flask.render_template("coprs/add_fedora_review.html", form=form)

+ 

+ 

  @coprs_ns.route("/<username>/add/")

  @coprs_ns.route("/g/<group_name>/add/")

  @login_required

@@ -9,13 +9,21 @@

  from coprs import models

  

  

- def parse_web_form_error(html_text):

+ def parse_web_form_error(html_text, variant="a"):

      """ return the list of form errors from failed form page """

      soup = BeautifulSoup(html_text, "html.parser")

-     alerts = soup.findAll('div', class_='alert alert-danger')

+ 

+     if variant == "a":

+         classes = "alert alert-danger"

+     elif variant == "b":

+         classes = "alert alert-danger alert-dismissable"

+ 

+     alerts = soup.findAll('div', class_=classes)

      assert len(alerts) == 1

      div = alerts[0]

-     return [li.text for li in div.find_all("li")]

+     if variant == "a":

+         return [li.text for li in div.find_all("li")]

+     return div.text.strip()

  

  

  class _RequestsInterface:

@@ -16,8 +16,11 @@

  from coprs.logic.coprs_logic import CoprsLogic, CoprDirsLogic

  from coprs.logic.actions_logic import ActionsLogic

  

+ from commands.create_chroot import create_chroot_function

+ 

  from tests.coprs_test_case import (CoprsTestCase, TransactionDecorator,

      new_app_context)

+ from tests.request_test_api import parse_web_form_error

  

  

  class TestMonitor(CoprsTestCase):
@@ -1071,3 +1074,32 @@

          _expected(actions[0], ["fedora-17-x86_64", "fedora-rawhide-i386"])

          _expected(actions[1], ["fedora-18-x86_64"])

          _expected(actions[2], ["fedora-17-x86_64"])

+ 

+     @pytest.mark.usefixtures("f_u1_ts_client", "f_mock_chroots", "f_db")

+     def test_fedora_review_project(self):

+         create_chroot_function(["fedora-rawhide-x86_64"])

+         route = "/coprs/{0}/new-fedora-review/".format(self.transaction_username)

+         resp = self.test_client.post(

+             route,

+             data={"name": "test-fedora-review"},

+             follow_redirects=False,

+         )

+         assert "user1/test-fedora-review/add_build" in resp.headers["Location"]

+         copr = self.models.Copr.query.get(1)

+         assert copr.full_name == "user1/test-fedora-review"

+         assert len(copr.active_chroots) == 1

+         assert copr.active_chroots[0].name == "fedora-rawhide-x86_64"

+         assert "Fedora Review tool" in copr.description

+         assert "You should ask the project owner" in copr.instructions

+         assert copr.fedora_review

+         assert copr.unlisted_on_hp

+ 

+         # re-request

+         resp = self.test_client.post(

+             route,

+             data={"name": "test-fedora-review"},

+             follow_redirects=True,

+         )

+         assert resp.status_code == 200  # error!

+         error = parse_web_form_error(resp.data, variant="b")

+         assert error == "Error in project config"

file modified
+7 -1
@@ -42,7 +42,13 @@

  #     This is to fix our Jenkins CI where we do not have all the build

  #     requirements for all our sub-components.  We can afford not listening to

  #     this error because our packaging CI would discover the problems anyways.

- disable=import-error

+ # too-few-public-methods

+ #     It's often useful to inherit from some (even library) class, and override

+ #     some static property or even just constructor.  This though makes PyLint

+ #     warn us if parent method doesn't provide the minimal amount of methods,

+ #     reported here: https://github.com/PyCQA/pylint/issues/4352

+ #     It's inconvenient to silence this per-class.

+ disable=import-error,too-few-public-methods

  

  [VARIABLES]

  # A regular expression matching names used for dummy variables (i.e. not used).

no initial comment

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 19c400c68812ade608ba1fdf47940e3e3529002e

3 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

rebased onto 239504c92bedf4574b6a4f565944d0756d0e5309

3 years ago

Metadata Update from @praiskup:
- Pull-request tagged with: needs-tests

3 years ago

Build succeeded.

Metadata Update from @praiskup:
- Pull-request tagged with: wip

3 years ago

rebased onto 02815a03766ba44c54e8bebb76c4266ced085884

3 years ago

Metadata Update from @praiskup:
- Pull-request untagged with: needs-tests, wip

3 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

2 new commits added

  • frontend: create project for Fedora Review
  • pylintrc: ignore too-few-public-methods warning
3 years ago

Build succeeded.

Please add a comment that this validator is intended for a fedora review project, otherwise if I did not see this PR, it is not clear to me why we need two quite similar validators (CoprUniqueNameValidator and CoprUniqueNameValidator2).

Otherwise, +1, thank you.

It is not intended for the Fedora Review projects only. :) It is meant to be a generic method, that should actually replace CoprUniqueNameValidator one day (but I would have to do more intrusive changes to use it everywhere that are not relevant to the purpose of this PR).

I am not sure whether it is a good idea to change the FlaskForm constructor parameters. I believe it works in this case and it isn't a blocker for me but

All forms that modify Copr project should inherit from this.

This will cause problems if CoprForm starts inheriting this class because CoprForm is used in APIv3 and we pass the formdata parameter to it.

I am not sure, are you trying to send the form to the same URL? In that case you can have just action=""

This doesn't use APIv3 but rather normal views, so I would prefer moving it somewhere else. Maybe test_coprs_general.py?

I am not sure, are you trying to send the form to the same URL? In that case you can have just action=""

It probably works, though it is against specs.

This doesn't use APIv3 but rather normal views, so I would prefer moving it somewhere else. Maybe test_coprs_general.py?

Ack.

This check is done in CoprsLogic.add so I think it shouldn't be necessary here.

The else branch can be easily dropped if we move the return flask.render_template("coprs/add_fedora_review.html", form=form) here

It probably requires more work here and there, though I'd really love to avoid the complicated broken factory methods in factory classes, that don't require objects but classes, etc. ... We can add arguments here wisely in future?

Well, I didn't want to render the form accidentally .. the add() method is too late.

I am not sure, are you trying to send the form to the same URL? In that case you can have just action=""

It probably works, though it is against specs.

Ah sorry, didn't know that. I was doing this wrong for a long time then. Though the spec says "The action and formaction content attributes, if specified" so maybe it is possible to not set it at all if submitting to the same page.

But it's just a minor thing ...

I think we can do something like this

__init__(formdata=None, obj=None, prefix='', data=None, meta=None, user=None, group=None, **kwargs)
    super().__init__(formdata=formdata, obj=obj, prefix=prefix', data=data, meta=meta, **kwargs)
   self.user = user
   self.group = group

That should be safe to use anywhere.

Hmm, I'd have to copy that statement (can't be removed at the end of the method). I have removed the else branch, but I think I liked the if-else more.

rebased onto 15a73c3

3 years ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

I agree, well .. I can't validate this at first sight - so I'd probably prefer to keep this on a separate commit so it is clear why we are adding the arguments .... if you don't mind too much. When I see how complicated the approach is, I'm not sure it is the best way anyway.

2 new commits added

  • frontend: create project for Fedora Review
  • pylintrc: ignore too-few-public-methods warning
3 years ago

Build succeeded.

We can leave it as is and deal with it when needed.
Just saying though, we sometimes need to pass the formdata so if CoprForm is going to inherit this form, we will have to figure it out :-)

We could also have

__init__(user=None, group=None, *args,**kwargs)
    super().__init__(*args, **kwargs)

which is much prettier and will allow us to do everything that we want to but messes up the order of parameters, so we will need to change our statements like

form = forms.CoprForm(data, meta={'csrf': False})

to

form = forms.CoprForm(formdata=data, meta={'csrf': False})

which I am okay with :-)

2 new commits added

  • frontend: create project for Fedora Review
  • pylintrc: ignore too-few-public-methods warning
3 years ago

init(user=None, group=None, args,*kwargs)

I did (basically) that now, good idea.

Build succeeded.

+1, thank you for the changes

Commit e61dcf1 fixes this pull-request

Pull-Request has been merged by frostyx

3 years ago

Pull-Request has been merged by frostyx

3 years ago