Example #1
0
def _check_private_issue_access(issue):
    """Check if user can access issue. Must be repo commiter
    or author to see private issues.
    :param issue: issue object
    :raises pagure.exceptions.APIError: when access denied
    """
    if (issue.private and not is_repo_committer(issue.project)
            and (not api_authenticated()
                 or not issue.user.user == flask.g.fas_user.username)):
        raise pagure.exceptions.APIError(403,
                                         error_code=APIERROR.EISSUENOTALLOWED)
Example #2
0
def _check_token(repo, project_token=True):
    """Check if token is valid for the repo
    :param repo: repository name
    :param project_token: set True when project token is required,
        otherwise any token can be used
    :raises pagure.exceptions.APIError: when token is not valid for repo
    """
    if api_authenticated():
        # if there is a project associated with the token, check it
        # if there is no project associated, check if it is required
        if (flask.g.token.project is not None
                and repo != flask.g.token.project) \
                or (flask.g.token.project is None and project_token):
            raise pagure.exceptions.APIError(401,
                                             error_code=APIERROR.EINVALIDTOK)
Example #3
0
def api_change_status_issue(repo, issueid, username=None):
    """
    Change issue status
    -------------------
    Change the status of an issue.

    ::

        POST /api/0/<repo>/issue/<issue id>/status

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/status

    Input
    ^^^^^

    +-------------+---------+--------------+------------------------------+
    | Key         | Type    | Optionality  | Description                  |
    +=============+=========+==============+==============================+
    | ``status``  | string  | Mandatory    | The new status of the issue  |
    +-------------+---------+--------------+------------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Successfully edited issue #1"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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 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)

    status = pagure.lib.get_issue_statuses(SESSION)
    form = pagure.forms.StatusForm(status=status, csrf_enabled=False)
    if form.validate_on_submit():
        new_status = form.status.data
        try:
            # Update status
            message = pagure.lib.edit_issue(
                SESSION,
                issue=issue,
                status=new_status,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            SESSION.commit()
            if message:
                output['message'] = message
            else:
                output['message'] = 'No changes'
        except pagure.exceptions.PagureException, err:
            raise pagure.exceptions.APIError(400,
                                             error_code=APIERROR.ENOCODE,
                                             error=str(err))
        except SQLAlchemyError, err:  # pragma: no cover
            SESSION.rollback()
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Example #4
0
def api_view_issue_comment(repo, issueid, commentid, username=None):
    """
    Comment of an issue
    --------------------
    Retrieve a specific comment of an issue.

    ::

        GET /api/0/<repo>/issue/<issue id>/comment/<comment id>

    ::

        GET /api/0/fork/<username>/<repo>/issue/<issue id>/comment/<comment id>

    The identifier provided can be either the unique identifier or the
    regular identifier used in the UI (for example ``24`` in
    ``/forks/user/test/issue/24``)

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "avatar_url": "https://seccdn.libravatar.org/avatar/...",
          "comment": "9",
          "comment_date": "2015-07-01 15:08",
          "date_created": "1435756127",
          "id": 464,
          "parent": null,
          "user": {
            "fullname": "P.-Y.C.",
            "name": "pingou"
          }
        }

    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    issue_id = issue_uid = None
    try:
        issue_id = int(issueid)
    except:
        issue_uid = issueid

    issue = pagure.lib.search_issues(SESSION,
                                     repo,
                                     issueid=issue_id,
                                     issueuid=issue_uid)

    if issue is None or issue.project != repo:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE)

    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(401,
                                             error_code=APIERROR.EINVALIDTOK)

    if issue.private and not is_repo_admin(issue.project) \
            and (not api_authenticated() or
                 not issue.user.user == flask.g.fas_user.username):
        raise pagure.exceptions.APIError(403,
                                         error_code=APIERROR.EISSUENOTALLOWED)

    comment = pagure.lib.get_issue_comment(SESSION, issue.uid, commentid)
    if not comment:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOCOMMENT)

    output = comment.to_json(public=True)
    output['avatar_url'] = pagure.lib.avatar_url(comment.user.user, size=16)
    output['comment_date'] = comment.date_created.strftime('%Y-%m-%d %H:%M')
    jsonout = flask.jsonify(output)
    return jsonout
Example #5
0
def api_view_issue(repo, issueid, username=None):
    """
    Issue information
    -----------------
    Retrieve information of a specific issue.

    ::

        GET /api/0/<repo>/issue/<issue id>

    ::

        GET /api/0/fork/<username>/<repo>/issue/<issue id>

    The identifier provided can be either the unique identifier or the
    regular identifier used in the UI (for example ``24`` in
    ``/forks/user/test/issue/24``)

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "assignee": null,
          "blocks": [],
          "comments": [],
          "content": "This issue needs attention",
          "date_created": "1431414800",
          "depends": [],
          "id": 1,
          "private": false,
          "status": "Open",
          "tags": [],
          "title": "test issue",
          "user": {
            "fullname": "PY C",
            "name": "pingou"
          }
        }

    """
    comments = flask.request.args.get('comments', True)
    if str(comments).lower() in ['0', 'False']:
        comments = False

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    issue_id = issue_uid = None
    try:
        issue_id = int(issueid)
    except:
        issue_uid = issueid

    issue = pagure.lib.search_issues(SESSION,
                                     repo,
                                     issueid=issue_id,
                                     issueuid=issue_uid)

    if issue is None or issue.project != repo:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE)

    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(401,
                                             error_code=APIERROR.EINVALIDTOK)

    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)

    jsonout = flask.jsonify(issue.to_json(public=True, with_comments=comments))
    return jsonout
Example #6
0
def api_view_issues(repo, username=None):
    """
    List project's issues
    ---------------------
    List issues of a project.

    ::

        GET /api/0/<repo>/issues

    ::

        GET /api/0/fork/<username>/<repo>/issues

    Parameters
    ^^^^^^^^^^

    +---------------+---------+--------------+---------------------------+
    | Key           | Type    | Optionality  | Description               |
    +===============+=========+==============+===========================+
    | ``status``    | string  | Optional     | | Filters the status of   |
    |               |         |              |   issues. Default:        |
    |               |         |              |   ``Open``                |
    +---------------+---------+--------------+---------------------------+
    | ``tags``      | string  | Optional     | | A list of tags you      |
    |               |         |              |   wish to filter. If      |
    |               |         |              |   you want to filter      |
    |               |         |              |   for issues not having   |
    |               |         |              |   a tag, add an           |
    |               |         |              |   exclamation mark in     |
    |               |         |              |   front of it             |
    +---------------+---------+--------------+---------------------------+
    | ``assignee``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by assignee             |
    +---------------+---------+--------------+---------------------------+
    | ``author``    | string  | Optional     | | Filter the issues       |
    |               |         |              |   by creator              |
    +---------------+---------+--------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "total_issues": 1,
          "issues": [
            {
              "assignee": null,
              "blocks": [],
              "comments": [],
              "content": "asd",
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    status = flask.request.args.get('status', None)
    tags = flask.request.args.getlist('tags')
    tags = [tag.strip() for tag in tags if tag.strip()]
    assignee = flask.request.args.get('assignee', None)
    author = flask.request.args.get('author', None)

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(401,
                                             error_code=APIERROR.EINVALIDTOK)
        private = flask.g.fas_user.username
    # If user is repo admin, show all tickets included the private ones
    if is_repo_admin(repo):
        private = None

    if status is not None:
        if status.lower() == 'closed':
            issues = pagure.lib.search_issues(
                SESSION,
                repo,
                closed=True,
                tags=tags,
                assignee=assignee,
                author=author,
                private=private,
            )
        else:
            issues = pagure.lib.search_issues(
                SESSION,
                repo,
                status=status,
                tags=tags,
                assignee=assignee,
                author=author,
                private=private,
            )
    else:
        issues = pagure.lib.search_issues(SESSION,
                                          repo,
                                          status='Open',
                                          tags=tags,
                                          assignee=assignee,
                                          author=author,
                                          private=private)

    jsonout = flask.jsonify({
        'total_issues':
        len(issues),
        'issues': [issue.to_json(public=True) for issue in issues],
        'args': {
            'status': status,
            'tags': tags,
            'assignee': assignee,
            'author': author,
        }
    })
    return jsonout
Example #7
0
def api_change_status_issue(repo, issueid, username=None):
    """
    Change issue status
    -------------------
    Change the status of an issue.

    ::

        POST /api/0/<repo>/issue/<issue id>/status

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/status

    Input
    ^^^^^

    +-------------+---------+--------------+------------------------------+
    | Key         | Type    | Optionality  | Description                  |
    +=============+=========+==============+==============================+
    | ``status``  | string  | Mandatory    | The new status of the issue  |
    +-------------+---------+--------------+------------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Successfully edited issue #1"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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)

    status = pagure.lib.get_issue_statuses(SESSION)
    form = pagure.forms.StatusForm(status=status, csrf_enabled=False)
    if form.validate_on_submit():
        new_status = form.status.data
        try:
            # Update status
            message = pagure.lib.edit_issue(
                SESSION,
                issue=issue,
                status=new_status,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            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
            SESSION.rollback()
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    jsonout = flask.jsonify(output)
    return jsonout
Example #8
0
def api_view_issue(repo, issueid, username=None):
    """
    Issue information
    -----------------
    Retrieve information of a specific issue.

    ::

        GET /api/0/<repo>/issue/<issue id>

    ::

        GET /api/0/fork/<username>/<repo>/issue/<issue id>

    The identifier provided can be either the unique identifier or the
    regular identifier used in the UI (for example ``24`` in
    ``/forks/user/test/issue/24``)

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "assignee": null,
          "blocks": [],
          "comments": [],
          "content": "This issue needs attention",
          "date_created": "1431414800",
          "depends": [],
          "id": 1,
          "private": false,
          "status": "Open",
          "tags": [],
          "title": "test issue",
          "user": {
            "fullname": "PY C",
            "name": "pingou"
          }
        }

    """
    comments = flask.request.args.get('comments', True)
    if str(comments).lower() in ['0', 'False']:
        comments = False

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    issue_id = issue_uid = None
    try:
        issue_id = int(issueid)
    except:
        issue_uid = issueid

    issue = pagure.lib.search_issues(
        SESSION, repo, issueid=issue_id, issueuid=issue_uid)

    if issue is None or issue.project != repo:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE)

    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(
                401, error_code=APIERROR.EINVALIDTOK)

    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)

    jsonout = flask.jsonify(
        issue.to_json(public=True, with_comments=comments))
    return jsonout
Example #9
0
def api_assign_issue(repo, issueid, username=None):
    """
    Assign an issue
    ---------------
    Assign an issue to someone.

    ::

        POST /api/0/<repo>/issue/<issue id>/assign

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/assign

    Input
    ^^^^^

    +--------------+----------+---------------+---------------------------+
    | Key          | Type     | Optionality   | Description               |
    +==============+==========+===============+===========================+
    | ``assignee`` | string   | Mandatory     | | The username of the user|
    |              |          |               |   to assign the issue to. |
    +--------------+----------+---------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Issue assigned"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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)

    form = pagure.forms.AssignIssueForm(csrf_enabled=False)
    if form.validate_on_submit():
        assignee = form.assignee.data
        try:
            # New comment
            message = pagure.lib.add_issue_assignee(
                SESSION,
                issue=issue,
                assignee=assignee,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            SESSION.commit()
            output['message'] = message
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    jsonout = flask.jsonify(output)
    return jsonout
Example #10
0
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/<repo>/issue/<issue id>/custom/<field>
        POST /api/0/<namespace>/<repo>/issue/<issue id>/custom/<field>

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/custom/<field>
        POST /api/0/fork/<username>/<namespace>/<repo>/issue/<issue id>/custom/<field>

    Input
    ^^^^^

    +----------------- +---------+--------------+-------------------------+
    | Key              | Type    | Optionality  | Description             |
    +==================+=========+==============+=========================+
    | ``value``        | string  | Optional     | The new value of the    |
    |                  |         |              | custom field of interest|
    +----------------- +---------+--------------+-------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Custom field 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
Example #11
0
def api_subscribe_issue(repo, issueid, username=None, namespace=None):
    """
    Subscribe to an issue
    ---------------------
    Allows someone to subscribe or unscribe to the notifications related to
    an issue.

    ::

        POST /api/0/<repo>/issue/<issue id>/subscribe
        POST /api/0/<namespace>/<repo>/issue/<issue id>/subscribe

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/subscribe
        POST /api/0/fork/<username>/<namespace>/<repo>/issue/<issue id>/subscribe

    Input
    ^^^^^

    +--------------+----------+---------------+---------------------------+
    | Key          | Type     | Optionality   | Description               |
    +==============+==========+===============+===========================+
    | ``status``   | boolean   | Mandatory    | | The subscription status |
    |              |          |               |   to subscribe or         |
    |              |          |               |   unsubscribe to the.     |
    |              |          |               |   issue.                  |
    +--------------+----------+---------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "User subscribed"
        }

    """
    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)

    form = pagure.forms.SubscribtionForm(csrf_enabled=False)
    if form.validate_on_submit():
        status = str(form.status.data).strip().lower() in ['1', 'true']
        try:
            # Toggle subscribtion
            message = pagure.lib.set_watch_obj(
                SESSION,
                user=flask.g.fas_user.username,
                obj=issue,
                watch_status=status
            )
            SESSION.commit()
            output['message'] = message
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors)

    jsonout = flask.jsonify(output)
    return jsonout
Example #12
0
def api_change_status_issue(repo, issueid, username=None):
    """
    Change issue status
    -------------------
    This endpoint can be used to change the status of an issue

    ::

        /api/0/<repo>/issue/<issue id>/status

        /api/0/fork/<username>/<repo>/issue/<issue id>/status

    Accepts POST queries only.

    :arg status: The new status of the specified issue

    Sample response:

    ::

        {
          "message": "Edited successfully issue #1"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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 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)

    status = pagure.lib.get_issue_statuses(SESSION)
    form = pagure.forms.StatusForm(status=status, csrf_enabled=False)
    if form.validate_on_submit():
        new_status = form.status.data
        try:
            # Update status
            message = pagure.lib.edit_issue(
                SESSION,
                issue=issue,
                status=new_status,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            SESSION.commit()
            if message:
                output['message'] = message
            else:
                output['message'] = 'No changes'
        except pagure.exceptions.PagureException, err:
            raise pagure.exceptions.APIError(
                400, error_code=APIERROR.ENOCODE, error=str(err))
        except SQLAlchemyError, err:  # pragma: no cover
            SESSION.rollback()
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Example #13
0
def api_view_issue(repo, issueid, username=None):
    """
    Issue information
    -----------------
    This endpoint can be used to retrieve information about a specific
    issue/ticket

    ::

        /api/0/<repo>/issue/<issue id>

        /api/0/fork/<username>/<repo>/issue/<issue id>

    Accepts GET queries only.

    Sample response:

    ::

        {
          "assignee": null,
          "blocks": [],
          "comments": [],
          "content": "This issue needs attention",
          "date_created": "1431414800",
          "depends": [],
          "id": 1,
          "private": false,
          "status": "Open",
          "tags": [],
          "title": "test issue",
          "user": {
            "fullname": "PY C",
            "name": "pingou"
          }
        }

    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    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 api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(
                401, error_code=APIERROR.EINVALIDTOK)

    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)

    jsonout = flask.jsonify(issue.to_json(public=True))
    return jsonout
Example #14
0
def api_view_issues(repo, username=None):
    """
    List project's issues
    ---------------------
    This endpoint can be used to retrieve the list of all issues of the
    specified project

    ::

        /api/0/<repo>/issues

        /api/0/fork/<username>/<repo>/issues

    Accepts GET queries only.

    :kwarg status: The status of the issues to return, default to 'Open'
    :kwarg tags: One or more tags to filter the issues returned.
        If you want to wish to filter for issues not having a specific tag
        you can mark the tag with an exclamation mark in front of it, for
        example to get all the issues not tagged as ``easyfix`` you can
        filter using the tag ``!easyfix``
    :kwarg assignee: Filters the issues returned by the user they are
        assigned to
    :kwarg author: Filters the issues returned by the user that opened the
        issue

    Sample response:

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            "status": null,
            "tags": []
          },
          "issues": [
            {
              "assignee": null,
              "blocks": [],
              "comments": [
                {
                  "comment": "bing",
                  "date_created": "1427441560",
                  "id": 379,
                  "parent": null,
                  "user": {
                    "fullname": "PY.C",
                    "name": "pingou"
                  }
                }
              ],
              "content": "bar",
              "date_created": "1427441555",
              "depends": [],
              "id": 1,
              "private": false,
              "status": "Open",
              "tags": [],
              "title": "foo",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            },
            {
              "assignee": null,
              "blocks": [],
              "comments": [],
              "content": "report",
              "date_created": "1427442076",
              "depends": [],
              "id": 2,
              "private": false,
              "status": "Open",
              "tags": [],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

        Second example:

        {
          "args": {
            "assignee": null,
            "author": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "issues": [
            {
              "assignee": null,
              "blocks": [],
              "comments": [],
              "content": "asd",
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    status = flask.request.args.get('status', None)
    tags = flask.request.args.getlist('tags')
    tags = [tag.strip() for tag in tags if tag.strip()]
    assignee = flask.request.args.get('assignee', None)
    author = flask.request.args.get('author', None)

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(
                401, error_code=APIERROR.EINVALIDTOK)
        private = flask.g.fas_user.username
    # If user is repo admin, show all tickets included the private ones
    if is_repo_admin(repo):
        private = None

    if status is not None:
        if status.lower() == 'closed':
            issues = pagure.lib.search_issues(
                SESSION,
                repo,
                closed=True,
                tags=tags,
                assignee=assignee,
                author=author,
                private=private,
            )
        else:
            issues = pagure.lib.search_issues(
                SESSION,
                repo,
                status=status,
                tags=tags,
                assignee=assignee,
                author=author,
                private=private,
            )
    else:
        issues = pagure.lib.search_issues(
            SESSION, repo, status='Open', tags=tags, assignee=assignee,
            author=author, private=private)

    jsonout = flask.jsonify({
        'issues': [issue.to_json(public=True) for issue in issues],
        'args': {
            'status': status,
            'tags': tags,
            'assignee': assignee,
            'author': author,
        }
    })
    return jsonout
Example #15
0
def api_comment_issue(repo, issueid, username=None):
    """
    Comment to an issue
    -------------------
    Add a comment to an issue.

    ::

        POST /api/0/<repo>/issue/<issue id>/comment

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/comment

    Input
    ^^^^^

    +--------------+----------+---------------+---------------------------+
    | Key          | Type     | Optionality   | Description               |
    +==============+==========+===============+===========================+
    | ``comment``  | string   | Mandatory     | | The comment to add to   |
    |              |          |               |   the issue               |
    +--------------+----------+---------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Comment added"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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 repo.fullname != flask.g.token.project.fullname:
        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)

    form = pagure.forms.CommentForm(csrf_enabled=False)
    if form.validate_on_submit():
        comment = form.comment.data
        try:
            # New comment
            message = pagure.lib.add_issue_comment(
                SESSION,
                issue=issue,
                comment=comment,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            SESSION.commit()
            output['message'] = message
        except SQLAlchemyError, err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Example #16
0
def api_comment_issue(repo, issueid, username=None):
    """
    Comment to an issue
    -------------------
    Add a comment to an issue.

    ::

        POST /api/0/<repo>/issue/<issue id>/comment

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/comment

    Input
    ^^^^^

    +--------------+----------+---------------+---------------------------+
    | Key          | Type     | Optionality   | Description               |
    +==============+==========+===============+===========================+
    | ``comment``  | string   | Mandatory     | | The comment to add to   |
    |              |          |               |   the issue               |
    +--------------+----------+---------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Comment added"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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 repo.fullname != flask.g.token.project.fullname:
        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)

    form = pagure.forms.CommentForm(csrf_enabled=False)
    if form.validate_on_submit():
        comment = form.comment.data
        try:
            # New comment
            message = pagure.lib.add_issue_comment(
                SESSION,
                issue=issue,
                comment=comment,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            SESSION.commit()
            output['message'] = message
        except SQLAlchemyError, err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Example #17
0
def api_view_issues(repo, username=None):
    """
    List project's issues
    ---------------------
    List issues of a project.

    ::

        GET /api/0/<repo>/issues

    ::

        GET /api/0/fork/<username>/<repo>/issues

    Parameters
    ^^^^^^^^^^

    +---------------+---------+--------------+---------------------------+
    | Key           | Type    | Optionality  | Description               |
    +===============+=========+==============+===========================+
    | ``status``    | string  | Optional     | | Filters the status of   |
    |               |         |              |   issues. Fetches all the |
    |               |         |              |   issues if status is     |
    |               |         |              |   ``all``. Default:       |
    |               |         |              |   ``Open``                |
    +---------------+---------+--------------+---------------------------+
    | ``tags``      | string  | Optional     | | A list of tags you      |
    |               |         |              |   wish to filter. If      |
    |               |         |              |   you want to filter      |
    |               |         |              |   for issues not having   |
    |               |         |              |   a tag, add an           |
    |               |         |              |   exclamation mark in     |
    |               |         |              |   front of it             |
    +---------------+---------+--------------+---------------------------+
    | ``assignee``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by assignee             |
    +---------------+---------+--------------+---------------------------+
    | ``author``    | string  | Optional     | | Filter the issues       |
    |               |         |              |   by creator              |
    +---------------+---------+--------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "total_issues": 1,
          "issues": [
            {
              "assignee": null,
              "blocks": [],
              "comments": [],
              "content": "asd",
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    status = flask.request.args.get('status', None)
    tags = flask.request.args.getlist('tags')
    tags = [tag.strip() for tag in tags if tag.strip()]
    assignee = flask.request.args.get('assignee', None)
    author = flask.request.args.get('author', None)

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(
                401, error_code=APIERROR.EINVALIDTOK)
        private = flask.g.fas_user.username
    # If user is repo admin, show all tickets included the private ones
    if is_repo_admin(repo):
        private = None

    if status is not None:
        params = {
            'session': SESSION,
            'repo': repo,
            'tags': tags,
            'assignee': assignee,
            'author': author,
            'private': private
        }
        if status.lower() == 'closed':
            params.update({'closed': True})
        elif status.lower() != 'all':
            params.update({'status': status})
        issues = pagure.lib.search_issues(**params)
    else:
        issues = pagure.lib.search_issues(
            SESSION, repo, status='Open', tags=tags, assignee=assignee,
            author=author, private=private)

    jsonout = flask.jsonify({
        'total_issues': len(issues),
        'issues': [issue.to_json(public=True) for issue in issues],
        'args': {
            'status': status,
            'tags': tags,
            'assignee': assignee,
            'author': author,
        }
    })
    return jsonout
Example #18
0
def api_subscribe_pull_request(repo, requestid, username=None, namespace=None):
    """
    Subscribe to an pull-request
    ----------------------------
    Allows someone to subscribe to or unsubscribe from the notifications
    related to a pull-request.

    ::

        POST /api/0/<repo>/pull-request/<request id>/subscribe
        POST /api/0/<namespace>/<repo>/pull-request/<request id>/subscribe

    ::

        POST /api/0/fork/<username>/<repo>/pull-request/<request id>/subscribe
        POST /api/0/fork/<username>/<namespace>/<repo>/pull-request/<request id>/subscribe

    Input
    ^^^^^

    +--------------+----------+---------------+---------------------------+
    | Key          | Type     | Optionality   | Description               |
    +==============+==========+===============+===========================+
    | ``status``   | boolean  | Mandatory     | The intended subscription |
    |              |          |               | status. ``true`` for      |
    |              |          |               | subscribing, ``false``    |
    |              |          |               | for unsubscribing.        |
    +--------------+----------+---------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "User subscribed"
        }

    """  # noqa

    repo = get_authorized_api_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('pull_requests', True):
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPULLREQUESTSDISABLED)

    if api_authenticated():
        if flask.g.token.project and repo != flask.g.token.project:
            raise pagure.exceptions.APIError(401,
                                             error_code=APIERROR.EINVALIDTOK)

    request = pagure.lib.search_pull_requests(SESSION,
                                              project_id=repo.id,
                                              requestid=requestid)

    if not request:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOREQ)

    form = pagure.forms.SubscribtionForm(csrf_enabled=False)
    if form.validate_on_submit():
        status = str(form.status.data).strip().lower() in ['1', 'true']
        try:
            # Toggle subscribtion
            message = pagure.lib.set_watch_obj(SESSION,
                                               user=flask.g.fas_user.username,
                                               obj=request,
                                               watch_status=status)
            SESSION.commit()
            output['message'] = message
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(400,
                                         error_code=APIERROR.EINVALIDREQ,
                                         errors=form.errors)

    jsonout = flask.jsonify(output)
    return jsonout
Example #19
0
def api_view_issue_comment(repo, issueid, commentid, username=None):
    """
    Comment of an issue
    --------------------
    Retrieve a specific comment of an issue.

    ::

        GET /api/0/<repo>/issue/<issue id>/comment/<comment id>

    ::

        GET /api/0/fork/<username>/<repo>/issue/<issue id>/comment/<comment id>

    The identifier provided can be either the unique identifier or the
    regular identifier used in the UI (for example ``24`` in
    ``/forks/user/test/issue/24``)

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "avatar_url": "https://seccdn.libravatar.org/avatar/...",
          "comment": "9",
          "comment_date": "2015-07-01 15:08",
          "date_created": "1435756127",
          "id": 464,
          "parent": null,
          "user": {
            "fullname": "P.-Y.C.",
            "name": "pingou"
          }
        }

    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    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)

    issue_id = issue_uid = None
    try:
        issue_id = int(issueid)
    except:
        issue_uid = issueid

    issue = pagure.lib.search_issues(
        SESSION, repo, issueid=issue_id, issueuid=issue_uid)

    if issue is None or issue.project != repo:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE)

    if api_authenticated():
        if repo != flask.g.token.project:
            raise pagure.exceptions.APIError(
                401, error_code=APIERROR.EINVALIDTOK)

    if issue.private and not is_repo_admin(issue.project) \
            and (not api_authenticated() or
                 not issue.user.user == flask.g.fas_user.username):
        raise pagure.exceptions.APIError(
            403, error_code=APIERROR.EISSUENOTALLOWED)

    comment = pagure.lib.get_issue_comment(SESSION, issue.uid, commentid)
    if not comment:
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.ENOCOMMENT)

    output = comment.to_json(public=True)
    output['avatar_url'] = pagure.lib.avatar_url_from_openid(
        comment.user.default_email, size=16)
    output['comment_date'] = comment.date_created.strftime(
        '%Y-%m-%d %H:%M:%S')
    jsonout = flask.jsonify(output)
    return jsonout
Example #20
0
def api_view_group(group):
    """
    Group information
    -----------------
    Use this endpoint to retrieve information about a specific group.

    ::

        GET /api/0/group/<group>

    ::

        GET /api/0/group/some_group_name


    ::

        GET /api/0/group/some_group_name?projects=1&acl=commit

    Input
    ^^^^^

    +------------------+---------+--------------+-----------------------------+
    | Key              | Type    | Optionality  | Description                 |
    +==================+=========+==============+=============================+
    | ``group name``   | str     | Mandatory    | The name of the group to    |
    |                  |         |              | retrieve information about. |
    +------------------+---------+--------------+-----------------------------+
    | ``projects``     | bool    | Optional     | Specifies whether to include|
    |                  |         |              | projects in the data        |
    |                  |         |              | returned.                   |
    +------------------+---------+--------------+-----------------------------+
    | ``acl``          | str     | Optional     | Filter the project returned |
    |                  |         |              | (if any) to those where the |
    |                  |         |              | has the specified ACL level.|
    |                  |         |              | Can be any of: ``admin``,   |
    |                  |         |              | ``commit`` or ``ticket``.   |
    +------------------+---------+--------------+-----------------------------+


    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "creator": {
            "default_email": "*****@*****.**",
            "emails": [
              "*****@*****.**"
            ],
            "fullname": "User1",
            "name": "user1"
          },
          "date_created": "1492011511",
          "description": "Some Group",
          "display_name": "Some Group",
          "group_type": "user",
          "members": [
            "user1",
            "user2"
          ],
          "name": "some_group_name"
        }

    ::

        {
          "creator": {
            "default_email": "*****@*****.**",
            "emails": [
              "*****@*****.**"
            ],
            "fullname": "User1",
            "name": "user1"
          },
          "date_created": "1492011511",
          "description": "Some Group",
          "display_name": "Some Group",
          "group_type": "user",
          "members": [
            "user1",
            "user2"
          ],
          "name": "some_group_name",
          "projects": [],
        }


    """  # noqa
    projects = flask.request.values.get('projects',
                                        '').strip().lower() in ['1', 'true']
    acl = flask.request.values.get('acl', '').strip().lower() or None
    if acl == 'ticket':
        acl = ['admin', 'commit', 'ticket']
    elif acl == 'commit':
        acl = ['commit', 'admin']
    elif acl:
        acl = [acl]

    group = pagure.lib.search_groups(SESSION, group_name=group)
    if not group:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOGROUP)

    output = group.to_json(public=(not pagure.api_authenticated()))
    if projects and not acl:
        output['projects'] = [
            project.to_json(public=True) for project in group.projects
        ]
    elif projects and acl:
        output['projects'] = [
            pg.project.to_json(public=True) for pg in group.projects_groups
            if pg.access in acl
        ]
    jsonout = flask.jsonify(output)
    jsonout.status_code = 200
    return jsonout
Example #21
0
def api_assign_issue(repo, issueid, username=None):
    """
    Assign an issue
    ---------------
    Assign an issue to someone.

    ::

        POST /api/0/<repo>/issue/<issue id>/assign

    ::

        POST /api/0/fork/<username>/<repo>/issue/<issue id>/assign

    Input
    ^^^^^

    +--------------+----------+---------------+---------------------------+
    | Key          | Type     | Optionality   | Description               |
    +==============+==========+===============+===========================+
    | ``assignee`` | string   | Mandatory     | | The username of the user|
    |              |          |               |   to assign the issue to. |
    +--------------+----------+---------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "message": "Issue assigned"
        }

    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    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)

    form = pagure.forms.AssignIssueForm(csrf_enabled=False)
    if form.validate_on_submit():
        assignee = form.assignee.data
        try:
            # New comment
            message = pagure.lib.add_issue_assignee(
                SESSION,
                issue=issue,
                assignee=assignee,
                user=flask.g.fas_user.username,
                ticketfolder=APP.config['TICKETS_FOLDER'],
            )
            SESSION.commit()
            output['message'] = message
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    jsonout = flask.jsonify(output)
    return jsonout
Example #22
0
def api_view_issues(repo, username=None, namespace=None):
    """
    List project's issues
    ---------------------
    List issues of a project.

    ::

        GET /api/0/<repo>/issues
        GET /api/0/<namespace>/<repo>/issues

    ::

        GET /api/0/fork/<username>/<repo>/issues
        GET /api/0/fork/<username>/<namespace>/<repo>/issues

    Parameters
    ^^^^^^^^^^

    +---------------+---------+--------------+---------------------------+
    | Key           | Type    | Optionality  | Description               |
    +===============+=========+==============+===========================+
    | ``status``    | string  | Optional     | | Filters the status of   |
    |               |         |              |   issues. Fetches all the |
    |               |         |              |   issues if status is     |
    |               |         |              |   ``all``. Default:       |
    |               |         |              |   ``Open``                |
    +---------------+---------+--------------+---------------------------+
    | ``tags``      | string  | Optional     | | A list of tags you      |
    |               |         |              |   wish to filter. If      |
    |               |         |              |   you want to filter      |
    |               |         |              |   for issues not having   |
    |               |         |              |   a tag, add an           |
    |               |         |              |   exclamation mark in     |
    |               |         |              |   front of it             |
    +---------------+---------+--------------+---------------------------+
    | ``assignee``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by assignee             |
    +---------------+---------+--------------+---------------------------+
    | ``author``    | string  | Optional     | | Filter the issues       |
    |               |         |              |   by creator              |
    +---------------+---------+--------------+---------------------------+
    | ``milestones``| list of | Optional     | | Filter the issues       |
    |               | strings |              |   by milestone            |
    +---------------+---------+--------------+---------------------------+
    | ``priority``  | string  | Optional     | | Filter the issues       |
    |               |         |              |   by priority             |
    +---------------+---------+--------------+---------------------------+
    | ``no_stones`` | boolean | Optional     | | If true returns only the|
    |               |         |              |   issues having no        |
    |               |         |              |   milestone, if false     |
    |               |         |              |   returns only the issues |
    |               |         |              |   having a milestone      |
    +---------------+---------+--------------+---------------------------+
    | ``since``     | string  | Optional     | | Filter the issues       |
    |               |         |              |   updated after this date.|
    |               |         |              |   The date can either be  |
    |               |         |              |   provided as an unix date|
    |               |         |              |   or in the format Y-M-D  |
    +---------------+---------+--------------+---------------------------+
    | ``order``     | string  | Optional     | | Set the ordering of the |
    |               |         |              |   issues. This can be     |
    |               |         |              |   ``asc`` or ``desc``.    |
    |               |         |              |   Default: ``desc``       |
    +---------------+---------+--------------+---------------------------+

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "args": {
            "assignee": null,
            "author": null,
            'milestones': [],
            'no_stones': null,
            'order': null,
            'priority': null,
            "since": null,
            "status": "Closed",
            "tags": [
              "0.1"
            ]
          },
          "total_issues": 1,
          "issues": [
            {
              "assignee": null,
              "blocks": ["1"],
              "comments": [],
              "content": "asd",
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

    """
    repo = _get_repo(repo, username, namespace)
    _check_issue_tracker(repo)
    _check_token(repo)

    assignee = flask.request.args.get('assignee', None)
    author = flask.request.args.get('author', None)
    milestone = flask.request.args.getlist('milestones', None)
    no_stones = flask.request.args.get('no_stones', None)
    if no_stones is not None:
        if str(no_stones).lower() in ['1', 'true', 't']:
            no_stones = True
        else:
            no_stones = False
    priority = flask.request.args.get('priority', None)
    since = flask.request.args.get('since', None)
    order = flask.request.args.get('order', None)
    status = flask.request.args.get('status', None)
    tags = flask.request.args.getlist('tags')
    tags = [tag.strip() for tag in tags if tag.strip()]

    priority_key = None
    if priority:
        found = False
        if priority in repo.priorities:
            found = True
            priority_key = int(priority)
        else:
            for key, val in repo.priorities.items():
                if val.lower() == priority.lower():
                    priority_key = key
                    found = True
                    break

        if not found:
            raise pagure.exceptions.APIError(
                400, error_code=APIERROR.EINVALIDPRIORITY)

    # Hide private tickets
    private = False
    # If user is authenticated, show him/her his/her private tickets
    if api_authenticated():
        private = flask.g.fas_user.username
    # If user is repo committer, show all tickets included the private ones
    if is_repo_committer(repo):
        private = None

    params = {
        'session': SESSION,
        'repo': repo,
        'tags': tags,
        'assignee': assignee,
        'author': author,
        'private': private,
        'milestones': milestone,
        'priority': priority_key,
        'order': order,
        'no_milestones': no_stones,
    }

    if status is not None:
        if status.lower() == 'all':
            params.update({'status': None})
        elif status.lower() == 'closed':
            params.update({'closed': True})
        else:
            params.update({'status': status})
    else:
        params.update({'status': 'Open'})

    updated_after = None
    if since:
        # Validate and convert the time
        if since.isdigit():
            # We assume its a timestamp, so convert it to datetime
            try:
                updated_after = datetime.datetime.fromtimestamp(int(since))
            except ValueError:
                raise pagure.exceptions.APIError(
                    400, error_code=APIERROR.ETIMESTAMP)
        else:
            # We assume datetime format, so validate it
            try:
                updated_after = datetime.datetime.strptime(since, '%Y-%m-%d')
            except ValueError:
                raise pagure.exceptions.APIError(400,
                                                 error_code=APIERROR.EDATETIME)

    params.update({'updated_after': updated_after})
    issues = pagure.lib.search_issues(**params)
    jsonout = flask.jsonify({
        'total_issues':
        len(issues),
        'issues': [issue.to_json(public=True) for issue in issues],
        'args': {
            'assignee': assignee,
            'author': author,
            'milestones': milestone,
            'no_stones': no_stones,
            'order': order,
            'priority': priority,
            'since': since,
            'status': status,
            'tags': tags,
        }
    })
    return jsonout