I've reviewed the issues raised in the code review:
1) copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first()
Really shouldn't need try/except wrapping, looking at the source it just returns None when the username doesn't exist, which is handled correctly, and empirical testing confirms that.
2) if form.description.raw_data and len(form.description.raw_data): copr.description = form.description.data etc.
Really does handle the optional field as it should, meaning you can delete e.g. description by passing an empty string in POST, and on the other hand when you don't pass the field at all, it will not be changed.
3) I've changed the chroot modification URL to /coprs/<username>/<coprname>/detail/<chrootname>/ as requested.
4) Added the API documentation.
--- coprs_frontend/coprs/forms.py | 15 ++++ coprs_frontend/coprs/templates/api.html | 98 ++++++++++++++++++++++++ coprs_frontend/coprs/views/api_ns/api_general.py | 90 ++++++++++++++++++++++ 3 files changed, 203 insertions(+)
diff --git a/coprs_frontend/coprs/forms.py b/coprs_frontend/coprs/forms.py index 133275a..6d77974 100644 --- a/coprs_frontend/coprs/forms.py +++ b/coprs_frontend/coprs/forms.py @@ -264,3 +264,18 @@ class PermissionsFormFactory(object): coerce=int))
return F + +class CoprModifyForm(wtf.Form): + description = wtforms.TextAreaField('Description', + validators=[wtforms.validators.Optional()]) + + instructions = wtforms.TextAreaField('Instructions', + validators=[wtforms.validators.Optional()]) + + repos = wtforms.TextAreaField('Repos', + validators=[UrlListValidator(), + wtforms.validators.Optional()], + filters=[StringListFilter()]) + +class ModifyChrootForm(wtf.Form): + buildroot_pkgs = wtforms.TextField('Additional packages to be always present in minimal buildroot') diff --git a/coprs_frontend/coprs/templates/api.html b/coprs_frontend/coprs/templates/api.html index 48f95ca..e835d0f 100644 --- a/coprs_frontend/coprs/templates/api.html +++ b/coprs_frontend/coprs/templates/api.html @@ -235,5 +235,103 @@ copr_url = http://copr.fedoraproject.org } </pre>
+ <h3>Copr Modification</h3> + + <p><span style="font-style:italic">Login required</span></p> + + <h4>URL:</h4> + <pre style="font-size:120%">/api/coprs/<username>/<coprname>/modify/</pre> + + <h4>URL parameters:</h4> + <ul> + <li><b>username</b> – Name of the user whose copr you'd like to modify</li> + <li><b>coprname</b> – Name of the copr you'd you'd like to modify</li> + </ul> + + <h4>Parameters sent by POST:</h4> + <ul> + <li><b>description</b> – Brief description of the project (Optional)</li> + <li><b>instructions</b> – Description of how your project can be + installed, etc. (Optional)</li> + <li><b>repos</b> – URL to additional yum repos, which can be used + during build. Space separated. (Optional)</li> + </ul> + + <h4>Result</h4> + <ul> + <li><b>description</b> – Same as above</li> + <li><b>instructions</b> – Same as above</li> + <li><b>repos</b> – Same as above</li> + </ul> + + <h4>Example result</h4> + <pre> + { + "output": "ok", + "repos": "foo", + "description": "bar", + "instructions": "baz" + } + </pre> + + <h3>Chroot Modification</h3> + + <p><span style="font-style:italic">Login required</span></p> + + <h4>URL:</h4> + <pre style="font-size:120%">/api/coprs/<username>/<coprname>/modify/<chrootname>/</pre> + + <h4>URL parameters:</h4> + <ul> + <li><b>username</b> – Name of the user whose chroot you'd like to modify</li> + <li><b>coprname</b> – Name of the copr whose chroot you'd like to modify</li> + <li><b>chrootname</b> – Name of the chroot you'd like to modify</li> + </ul> + + <h4>Parameters sent by POST:</h4> + <ul> + <li><b>buildroot_pkgs</b> – Additional packages to be always present in minimal + buildroot</li> + </ul> + + <h4>Result</h4> + <ul> + <li><b>buildroot_pkgs</b> – Same as above</li> + </ul> + + <h4>Example result</h4> + <pre> + { + "output": "ok" + "buildroot_pkgs": "scl-utils-build", + } + </pre> + + <h3>Chroot details</h3> + + <h4>URL:</h4> + <pre style="font-size:120%">/api/coprs/<username>/<coprname>/detail/<chrootname>/</pre> + + <h4>URL parameters:</h4> + <ul> + <li><b>username</b> – Name of the user whose chroot you'd like to know details of</li> + <li><b>coprname</b> – Name of the copr whose chroot you'd like to know details of</li> + <li><b>chrootname</b> – Name of the chroot you'd like to know details of</li> + </ul> + + <h4>Result</h4> + <ul> + <li><b>buildroot_pkgs</b> – Additional packages to be always present in minimal + buildroot</li> + </ul> + + <h4>Example result</h4> + <pre> + { + "output": "ok" + "buildroot_pkgs": "scl-utils-build", + } + </pre> + </div> {% endblock %} diff --git a/coprs_frontend/coprs/views/api_ns/api_general.py b/coprs_frontend/coprs/views/api_ns/api_general.py index 063593d..d43d0dd 100644 --- a/coprs_frontend/coprs/views/api_ns/api_general.py +++ b/coprs_frontend/coprs/views/api_ns/api_general.py @@ -250,3 +250,93 @@ def build_status(build_id): jsonout = flask.jsonify(output) jsonout.status_code = httpcode return jsonout + +@api_ns.route('/coprs/<username>/<coprname>/modify/', methods=["POST"]) +@api_login_required +def copr_modify(username, coprname): + form = forms.CoprModifyForm(csrf_enabled=False) + copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first() + + if copr is None: + output = {'output': 'notok', 'error': 'Invalid copr name or username'} + httpcode = 500 + elif not form.validate_on_submit(): + output = {'output': 'notok', 'error': 'Invalid request'} + httpcode = 500 + else: + # .raw_data needs to be inspected to figure out whether the field + # was not sent or was sent empty + if form.description.raw_data and len(form.description.raw_data): + copr.description = form.description.data + if form.instructions.raw_data and len(form.instructions.raw_data): + copr.instructions = form.instructions.data + if form.repos.raw_data and len(form.repos.raw_data): + copr.repos = form.repos.data + + try: + coprs_logic.CoprsLogic.update(flask.g.user, copr) + except (exceptions.ActionInProgressException, exceptions.InsufficientRightsException) as e: + db.session.rollback() + + output = {'output': 'notok', 'error': str(e)} + httpcode = 500 + else: + db.session.commit() + + output = {'output': 'ok', + 'description': copr.description, + 'instructions': copr.instructions, + 'repos': copr.repos} + httpcode = 200 + + jsonout = flask.jsonify(output) + jsonout.status_code = httpcode + return jsonout + +@api_ns.route('/coprs/<username>/<coprname>/modify/<chrootname>/', methods=["POST"]) +@api_login_required +def copr_modify_chroot(username, coprname, chrootname): + form = forms.ModifyChrootForm(csrf_enabled=False) + copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first() + chroot = coprs_logic.MockChrootsLogic.get_from_name(chrootname, active_only=True).first() + + if copr is None: + output = {'output': 'notok', 'error': 'Invalid copr name or username'} + httpcode = 500 + elif chroot is None: + output = {'output': 'notok', 'error': 'Invalid chroot name'} + httpcode = 500 + elif not form.validate_on_submit(): + output = {'output': 'notok', 'error': 'Invalid request'} + httpcode = 500 + else: + coprs_logic.CoprChrootsLogic.update_buildroot_pkgs(copr, chroot, form.buildroot_pkgs.data) + db.session.commit() + + ch = copr.check_copr_chroot(chroot) + output = {'output': 'ok', 'buildroot_pkgs': ch.buildroot_pkgs} + httpcode = 200 + + jsonout = flask.jsonify(output) + jsonout.status_code = httpcode + return jsonout + +@api_ns.route('/coprs/<username>/<coprname>/detail/<chrootname>/', methods=["POST"]) +def copr_chroot_details(username, coprname, chrootname): + copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first() + chroot = coprs_logic.MockChrootsLogic.get_from_name(chrootname, active_only=True).first() + + if copr is None: + output = {'output': 'notok', 'error': 'Invalid copr name or username'} + httpcode = 500 + elif chroot is None: + output = {'output': 'notok', 'error': 'Invalid chroot name'} + httpcode = 500 + else: + ch = copr.check_copr_chroot(chroot) + output = {'output': 'ok', 'buildroot_pkgs': ch.buildroot_pkgs} + httpcode = 200 + + jsonout = flask.jsonify(output) + jsonout.status_code = httpcode + return jsonout