From 7ef397a0b6a38d5b16328a1ca8dc93d23bc71f1c Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Dec 22 2016 14:07:18 +0000 Subject: Add a new API endpoint to set or reset the custom field of an issue Fixes https://pagure.io/pagure/issue/1607 --- diff --git a/pagure/api/__init__.py b/pagure/api/__init__.py index eb591fe..3bc2f23 100644 --- a/pagure/api/__init__.py +++ b/pagure/api/__init__.py @@ -80,6 +80,9 @@ class APIERROR(enum.Enum): 'instance' ETIMESTAMP = 'Invalid timestamp format' EDATETIME = 'Invalid datetime format' + EINVALIDISSUEFIELD = 'Invalid custom field submitted' + EINVALIDISSUEFIELD_LINK = 'Invalid custom field submitted, the value '\ + 'is not a link' def check_api_acls(acls, optional=False): diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 42b6685..78eea11 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -16,7 +16,9 @@ from sqlalchemy.exc import SQLAlchemyError import pagure import pagure.exceptions import pagure.lib -from pagure import APP, SESSION, is_repo_admin, api_authenticated +from pagure import ( + APP, SESSION, is_repo_admin, api_authenticated, urlpattern +) from pagure.api import ( API, api_method, api_login_required, api_login_optional, APIERROR ) @@ -952,3 +954,116 @@ def api_subscribe_issue(repo, issueid, username=None, namespace=None): jsonout = flask.jsonify(output) return jsonout + + +@API.route('//issue//custom/', methods=['POST']) +@API.route( + '///issue//custom/', + methods=['POST']) +@API.route( + '/fork///issue//custom/', + methods=['POST']) +@API.route( + '/fork////issue//custom/', + methods=['POST']) +@api_login_required(acls=['issue_update_custom_fields', 'issue_update']) +@api_method +def api_update_custom_field( + repo, issueid, field, username=None, namespace=None): + """ + Update custom field + ------------------- + Update or reset the content of a custom field associated to an issue. + + :: + + POST /api/0//issue//custom/ + POST /api/0///issue//custom/ + + :: + + POST /api/0/fork///issue//custom/ + POST /api/0/fork////issue//custom/ + + Input + ^^^^^ + + +----------------- +---------+--------------+-------------------------+ + | Key | Type | Optionality | Description | + +==================+=========+==============+=========================+ + | ``value`` | string | Optional | The new value of the | + | | | | custom field of interest| + +----------------- +---------+--------------+-------------------------+ + + Sample response + ^^^^^^^^^^^^^^^ + + :: + + { + "message": "Custom key adjusted" + } + + """ + repo = pagure.lib.get_project( + SESSION, repo, user=username, namespace=namespace) + + output = {} + + if repo is None: + raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT) + + if not repo.settings.get('issue_tracker', True): + raise pagure.exceptions.APIError( + 404, error_code=APIERROR.ETRACKERDISABLED) + + if api_authenticated(): + if repo != flask.g.token.project: + raise pagure.exceptions.APIError( + 401, error_code=APIERROR.EINVALIDTOK) + + issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) + + if issue is None or issue.project != repo: + raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE) + + if issue.private and not is_repo_admin(repo) \ + and (not api_authenticated() or + not issue.user.user == flask.g.fas_user.username): + raise pagure.exceptions.APIError( + 403, error_code=APIERROR.EISSUENOTALLOWED) + + fields = {k.name: k for k in repo.issue_keys} + if field not in fields: + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDISSUEFIELD) + + key = fields[field] + value = flask.request.form.get('value') + if value: + if key.key_type == 'link': + links = value.split(',') + for link in links: + link = link.replace(' ', '') + if not urlpattern.match(link): + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDISSUEFIELD_LINK) + try: + message = pagure.lib.set_custom_key_value( + SESSION, issue, key, value) + + SESSION.commit() + if message: + output['message'] = message + else: + output['message'] = 'No changes' + except pagure.exceptions.PagureException as err: + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.ENOCODE, error=str(err)) + except SQLAlchemyError as err: # pragma: no cover + print err + SESSION.rollback() + raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) + + jsonout = flask.jsonify(output) + return jsonout diff --git a/pagure/default_config.py b/pagure/default_config.py index da90503..6dff333 100644 --- a/pagure/default_config.py +++ b/pagure/default_config.py @@ -218,6 +218,7 @@ ACLS = { 'pull_request_merge': 'Merge a pull-request of this project', 'issue_subscribe': 'Subscribe the user with this token to an issue', 'issue_update': 'Update an issue, status, comments, custom fields...', + 'issue_update_custom_fields': 'Update the custom fields of an issue', } # Bootstrap URLS