Blame pagure/api/project.py

Pierre-Yves Chibon a1265e
# -*- coding: utf-8 -*-
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
"""
Pierre-Yves Chibon 01d0f2
 (c) 2015-2019 - Copyright Red Hat Inc
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
 Authors:
Pierre-Yves Chibon a1265e
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
"""
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 67d1cc
from __future__ import unicode_literals, absolute_import
Aurélien Bompard dcf6f6
Pierre-Yves Chibon a1265e
import flask
Pierre-Yves Chibon b130e5
import logging
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 9f9b1e
from sqlalchemy.exc import SQLAlchemyError
mprahl 7d811e
from six import string_types
Pierre-Yves Chibon 8715b5
from pygit2 import GitError, Repository
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon a1265e
import pagure
Pierre-Yves Chibon 8715b5
import pagure.forms
Pierre-Yves Chibon a1265e
import pagure.exceptions
Matt Prahl cd940d
import pagure.lib.git
Pierre-Yves Chibon 930073
import pagure.lib.query
Pierre-Yves Chibon b130e5
import pagure.utils
Pierre-Yves Chibon 9c2953
from pagure.api import (
Pierre-Yves Chibon 9c2953
    API,
Pierre-Yves Chibon 9c2953
    api_method,
Pierre-Yves Chibon 9c2953
    APIERROR,
Pierre-Yves Chibon 9c2953
    api_login_required,
Pierre-Yves Chibon 9c2953
    get_authorized_api_project,
Pierre-Yves Chibon 9c2953
    api_login_optional,
Pierre-Yves Chibon 9c2953
    get_request_data,
Pierre-Yves Chibon 9c2953
    get_page,
Pierre-Yves Chibon 9c2953
    get_per_page,
Pierre-Yves Chibon 9c2953
)
Pierre-Yves Chibon 01d0f2
from pagure.api.utils import _get_repo, _check_token
Pierre-Yves Chibon b130e5
from pagure.config import config as pagure_config
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
_log = logging.getLogger(__name__)
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/git/tags")</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/git/tags")</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/git/tags")</repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>/git/tags")</repo></namespace></username>
Pierre-Yves Chibon a1265e
@api_method
Pierre-Yves Chibon 34ece4
def api_git_tags(repo, username=None, namespace=None):
Pierre-Yves Chibon a1265e
    """
Pierre-Yves Chibon a1265e
    Project git tags
Pierre-Yves Chibon a1265e
    ----------------
Lei Yang 566c51
    List the tags made on the project Git repository.
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
    ::
Pierre-Yves Chibon a1265e
Lei Yang 566c51
        GET /api/0/<repo>/git/tags</repo>
Pierre-Yves Chibon 34ece4
        GET /api/0/<namespace>/<repo>/git/tags</repo></namespace>
Pierre-Yves Chibon a1265e
Lei Yang 566c51
    ::
Lei Yang 566c51
Lei Yang 566c51
        GET /api/0/fork/<username>/<repo>/git/tags</repo></username>
Pierre-Yves Chibon 34ece4
        GET /api/0/fork/<username>/<namespace>/<repo>/git/tags</repo></namespace></username>
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 06f5ad
    Parameters
Pierre-Yves Chibon 06f5ad
    ^^^^^^^^^^
Pierre-Yves Chibon 06f5ad
Pierre-Yves Chibon 06f5ad
    +-----------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 06f5ad
    | Key             | Type     | Optionality   | Description              |
Pierre-Yves Chibon 06f5ad
    +=================+==========+===============+==========================+
Pierre-Yves Chibon 06f5ad
    | ``with_commits``| string   | Optional      | | Include the commit hash|
Pierre-Yves Chibon 06f5ad
    |                 |          |               |   corresponding to the   |
Pierre-Yves Chibon 06f5ad
    |                 |          |               |   tags found in the repo |
Pierre-Yves Chibon 06f5ad
    +-----------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 06f5ad
Lei Yang 566c51
    Sample response
Lei Yang 566c51
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
    ::
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon a1265e
        {
Pierre-Yves Chibon 7a734f
          "total_tags": 2,
Pierre-Yves Chibon adc200
          "tags": ["0.0.1", "0.0.2"]
Pierre-Yves Chibon a1265e
        }
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 06f5ad
Pierre-Yves Chibon 06f5ad
        {
Pierre-Yves Chibon 06f5ad
          "total_tags": 2,
Pierre-Yves Chibon 06f5ad
          "tags": {
Pierre-Yves Chibon 06f5ad
            "0.0.1": "bb8fa2aa199da08d6085e1c9badc3d83d188d38c",
Pierre-Yves Chibon 06f5ad
            "0.0.2": "d16fe107eca31a1bdd66fb32c6a5c568e45b627e"
Pierre-Yves Chibon 06f5ad
          }
Pierre-Yves Chibon 06f5ad
        }
Pierre-Yves Chibon 06f5ad
Pierre-Yves Chibon a1265e
    """
Aurélien Bompard 619e2a
    with_commits = pagure.utils.is_true(
Pierre-Yves Chibon 9c2953
        flask.request.values.get("with_commits", False)
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 06f5ad
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 06f5ad
    tags = pagure.lib.git.get_git_tags(repo, with_commits=with_commits)
Pierre-Yves Chibon a1265e
Pierre-Yves Chibon 9c2953
    jsonout = flask.jsonify({"total_tags": len(tags), "tags": tags})
Pierre-Yves Chibon a1265e
    return jsonout
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/watchers")</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/watchers")</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/watchers")</repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>/watchers")</repo></namespace></username>
Matt Prahl 733eaa
@api_method
Matt Prahl 733eaa
def api_project_watchers(repo, username=None, namespace=None):
Pierre-Yves Chibon 9c2953
    """
Matt Prahl 733eaa
    Project watchers
Matt Prahl 733eaa
    ----------------
Matt Prahl 733eaa
    List the watchers on the project.
Matt Prahl 733eaa
Matt Prahl 733eaa
    ::
Matt Prahl 733eaa
Matt Prahl 733eaa
        GET /api/0/<repo>/watchers</repo>
Matt Prahl 733eaa
        GET /api/0/<namespace>/<repo>/watchers</repo></namespace>
Matt Prahl 733eaa
Matt Prahl 733eaa
    ::
Matt Prahl 733eaa
Matt Prahl 733eaa
        GET /api/0/fork/<username>/<repo>/watchers</repo></username>
Matt Prahl 733eaa
        GET /api/0/fork/<username>/<namespace>/<repo>/watchers</repo></namespace></username>
Matt Prahl 733eaa
Matt Prahl 733eaa
    Sample response
Matt Prahl 733eaa
    ^^^^^^^^^^^^^^^
Matt Prahl 733eaa
Matt Prahl 733eaa
    ::
Matt Prahl 733eaa
Matt Prahl 733eaa
        {
Matt Prahl 733eaa
            "total_watchers": 1,
Matt Prahl 733eaa
            "watchers": {
Matt Prahl 733eaa
                "mprahl": [
Matt Prahl 733eaa
                    "issues",
Matt Prahl 733eaa
                    "commits"
Matt Prahl 733eaa
                ]
Matt Prahl 733eaa
            }
Matt Prahl 733eaa
        }
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Matt Prahl 733eaa
Pierre-Yves Chibon 12d7b9
    implicit_watch_users = set([repo.user.username])
Pierre-Yves Chibon 12d7b9
    for access_type in repo.access_users:
Pierre-Yves Chibon 12d7b9
        implicit_watch_users = implicit_watch_users.union(
Pierre-Yves Chibon 12d7b9
            set([user.username for user in repo.access_users[access_type]])
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon fd3701
Matt Prahl 733eaa
    watching_users_to_watch_level = {}
Matt Prahl 733eaa
    for implicit_watch_user in implicit_watch_users:
Pierre-Yves Chibon 930073
        user_watch_level = pagure.lib.query.get_watch_level_on_repo(
Pierre-Yves Chibon 9c2953
            flask.g.session, implicit_watch_user, repo
Pierre-Yves Chibon 9c2953
        )
Matt Prahl 733eaa
        watching_users_to_watch_level[implicit_watch_user] = user_watch_level
Matt Prahl 733eaa
Ralph Bean e69883
    for access_type in repo.access_groups.keys():
Pierre-Yves Chibon 9c2953
        group_names = [
Pierre-Yves Chibon 9c2953
            "@" + group.group_name for group in repo.access_groups[access_type]
Pierre-Yves Chibon 9c2953
        ]
Ralph Bean e69883
        for group_name in group_names:
Ralph Bean 02a9ce
            if group_name not in watching_users_to_watch_level:
Ralph Bean e69883
                watching_users_to_watch_level[group_name] = set()
Pierre-Yves Chibon 930073
            # By the logic in pagure.lib.query.get_watch_level_on_repo, group
Pierre-Yves Chibon 930073
            # members only by default watch issues.  If they want to watch
Pierre-Yves Chibon 930073
            # commits they have to explicitly subscribe.
Pierre-Yves Chibon 9c2953
            watching_users_to_watch_level[group_name].add("issues")
Ralph Bean e69883
Pierre-Yves Chibon a55479
    for key in watching_users_to_watch_level:
Pierre-Yves Chibon a55479
        watching_users_to_watch_level[key] = list(
Pierre-Yves Chibon 9c2953
            watching_users_to_watch_level[key]
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon a55479
Matt Prahl 733eaa
    # Get the explicit watch statuses
Matt Prahl 733eaa
    for watcher in repo.watchers:
Matt Prahl 733eaa
        if watcher.watch_issues or watcher.watch_commits:
Pierre-Yves Chibon 9c2953
            watching_users_to_watch_level[
Pierre-Yves Chibon 9c2953
                watcher.user.username
Pierre-Yves Chibon 930073
            ] = pagure.lib.query.get_watch_level_on_repo(
Pierre-Yves Chibon 9c2953
                flask.g.session, watcher.user.username, repo
Pierre-Yves Chibon 9c2953
            )
Matt Prahl 733eaa
        else:
Matt Prahl 733eaa
            if watcher.user.username in watching_users_to_watch_level:
Matt Prahl 733eaa
                watching_users_to_watch_level.pop(watcher.user.username, None)
Matt Prahl 733eaa
Pierre-Yves Chibon 9c2953
    return flask.jsonify(
Pierre-Yves Chibon 9c2953
        {
Pierre-Yves Chibon 9c2953
            "total_watchers": len(watching_users_to_watch_level),
Pierre-Yves Chibon 9c2953
            "watchers": watching_users_to_watch_level,
Pierre-Yves Chibon 9c2953
        }
Pierre-Yves Chibon 9c2953
    )
Matt Prahl 733eaa
Matt Prahl 733eaa
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/git/urls")</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/git/urls")</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/git/urls")</repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>/git/urls")</repo></namespace></username>
Matt Prahl ad5147
@api_login_optional()
Matt Prahl ad5147
@api_method
Matt Prahl ad5147
def api_project_git_urls(repo, username=None, namespace=None):
Pierre-Yves Chibon 9c2953
    """
Matt Prahl ad5147
    Project Git URLs
Matt Prahl ad5147
    ----------------
Matt Prahl ad5147
    List the Git URLS on the project.
Matt Prahl ad5147
Matt Prahl ad5147
    ::
Matt Prahl ad5147
Matt Prahl ad5147
        GET /api/0/<repo>/git/urls</repo>
Matt Prahl ad5147
        GET /api/0/<namespace>/<repo>/git/urls</repo></namespace>
Matt Prahl ad5147
Matt Prahl ad5147
    ::
Matt Prahl ad5147
Matt Prahl ad5147
        GET /api/0/fork/<username>/<repo>/git/urls</repo></username>
Matt Prahl ad5147
        GET /api/0/fork/<username>/<namespace>/<repo>/git/urls</repo></namespace></username>
Matt Prahl ad5147
Matt Prahl ad5147
    Sample response
Matt Prahl ad5147
    ^^^^^^^^^^^^^^^
Matt Prahl ad5147
Matt Prahl ad5147
    ::
Matt Prahl ad5147
Matt Prahl ad5147
        {
Matt Prahl ad5147
            "total_urls": 2,
Matt Prahl ad5147
            "urls": {
Matt Prahl ad5147
                "ssh": "ssh://git@pagure.io/mprahl-test123.git",
Matt Prahl ad5147
                "git": "https://pagure.io/mprahl-test123.git"
Matt Prahl ad5147
            }
Matt Prahl ad5147
        }
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 7f5772
Pierre-Yves Chibon 01d0f2
    git_urls = {}
Pierre-Yves Chibon 9c2953
    git_url_ssh = pagure_config.get("GIT_URL_SSH")
Pierre-Yves Chibon 8d8fb0
    if pagure.utils.api_authenticated() and git_url_ssh:
Pierre-Yves Chibon 7f5772
        try:
Pierre-Yves Chibon 7f5772
            git_url_ssh = git_url_ssh.format(
Pierre-Yves Chibon 9c2953
                username=flask.g.fas_user.username
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 7f5772
        except (KeyError, IndexError):
Pierre-Yves Chibon 7f5772
            pass
Pierre-Yves Chibon 7f5772
Pierre-Yves Chibon 7f5772
    if git_url_ssh:
Pierre-Yves Chibon 9c2953
        git_urls["ssh"] = "{0}{1}.git".format(git_url_ssh, repo.fullname)
Pierre-Yves Chibon 9c2953
    if pagure_config.get("GIT_URL_GIT"):
Pierre-Yves Chibon 9c2953
        git_urls["git"] = "{0}{1}.git".format(
Pierre-Yves Chibon 9c2953
            pagure_config["GIT_URL_GIT"], repo.fullname
Pierre-Yves Chibon 9c2953
        )
Matt Prahl ad5147
Pierre-Yves Chibon 9c2953
    return flask.jsonify({"total_urls": len(git_urls), "urls": git_urls})
Matt Prahl ad5147
Matt Prahl ad5147
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/git/branches")</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/git/branches")</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/git/branches")</repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>/git/branches")</repo></namespace></username>
Matt Prahl 3114c1
@api_method
Matt Prahl cd940d
def api_git_branches(repo, username=None, namespace=None):
Pierre-Yves Chibon 9c2953
    """
Matt Prahl 398dcf
    List project branches
Matt Prahl 398dcf
    ---------------------
Matt Prahl cd940d
    List the branches associated with a Pagure git repository
Matt Prahl cd940d
Matt Prahl cd940d
    ::
Matt Prahl cd940d
Matt Prahl cd940d
        GET /api/0/<repo>/git/branches</repo>
Matt Prahl cd940d
        GET /api/0/<namespace>/<repo>/git/branches</repo></namespace>
Matt Prahl cd940d
Matt Prahl cd940d
    ::
Matt Prahl cd940d
Matt Prahl cd940d
        GET /api/0/fork/<username>/<repo>/git/branches</repo></username>
Matt Prahl cd940d
        GET /api/0/fork/<username>/<namespace>/<repo>/git/branches</repo></namespace></username>
Matt Prahl cd940d
Brian Stinson 4f81f7
    Parameters
Brian Stinson 4f81f7
    ^^^^^^^^^^
Brian Stinson 4f81f7
Brian Stinson 4f81f7
    +-----------------+----------+---------------+--------------------------+
Brian Stinson 4f81f7
    | Key             | Type     | Optionality   | Description              |
Brian Stinson 4f81f7
    +=================+==========+===============+==========================+
Julen Landa Alustiza 444a82
    | ``with_commits``| boolean  | Optional      | | Include the commit hash|
Brian Stinson 4f81f7
    |                 |          |               |   corresponding to the   |
Brian Stinson 4f81f7
    |                 |          |               |   HEAD of each branch    |
Brian Stinson 4f81f7
    +-----------------+----------+---------------+--------------------------+
Brian Stinson 4f81f7
Matt Prahl cd940d
    Sample response
Matt Prahl cd940d
    ^^^^^^^^^^^^^^^
Matt Prahl cd940d
Matt Prahl cd940d
    ::
Matt Prahl cd940d
Matt Prahl cd940d
        {
Matt Prahl cd940d
          "total_branches": 2,
Matt Prahl cd940d
          "branches": ["master", "dev"]
Matt Prahl cd940d
        }
Matt Prahl cd940d
Brian Stinson 4f81f7
        {
Brian Stinson 4f81f7
          "total_branches": 2,
Brian Stinson 4f81f7
          "branches": {
Brian Stinson 4f81f7
            "master": "16ae2a4df107658b52750063ae203f978cf02ff7",
Brian Stinson 4f81f7
            "dev": "8351c460167a41defc393f5b6c1d51fe1b3b82b8"
Brian Stinson 4f81f7
          }
Brian Stinson 4f81f7
        }
Brian Stinson 4f81f7
Pierre-Yves Chibon 9c2953
    """
Brian Stinson 4f81f7
Brian Stinson 4f81f7
    with_commits = pagure.utils.is_true(
Brian Stinson 4f81f7
        flask.request.values.get("with_commits", False)
Brian Stinson 4f81f7
    )
Brian Stinson 4f81f7
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Matt Prahl cd940d
Brian Stinson 4f81f7
    branches = pagure.lib.git.get_git_branches(repo, with_commits=with_commits)
Matt Prahl cd940d
Matt Prahl cd940d
    return flask.jsonify(
Pierre-Yves Chibon 9c2953
        {"total_branches": len(branches), "branches": branches}
Matt Prahl cd940d
    )
Matt Prahl cd940d
Matt Prahl cd940d
Pierre-Yves Chibon 9c2953
@API.route("/projects")
Pierre-Yves Chibon 47269d
@api_method
Pierre-Yves Chibon 47269d
def api_projects():
Pierre-Yves Chibon 47269d
    """
Pierre-Yves Chibon 47269d
    List projects
Pierre-Yves Chibon 47269d
    --------------
Pierre-Yves Chibon 47269d
    Search projects given the specified criterias.
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
    ::
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
        GET /api/0/projects
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
    ::
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
        GET /api/0/projects?tags=fedora-infra
Pierre-Yves Chibon 47269d
Matt Prahl a01c25
    ::
Matt Prahl a01c25
Matt Prahl a01c25
        GET /api/0/projects?page=1&per_page=50
Matt Prahl a01c25
Pierre-Yves Chibon 47269d
    Parameters
Pierre-Yves Chibon 47269d
    ^^^^^^^^^^
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
    +---------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 47269d
    | Key           | Type     | Optionality   | Description              |
Pierre-Yves Chibon 47269d
    +===============+==========+===============+==========================+
Pierre-Yves Chibon 47269d
    | ``tags``      | string   | Optional      | | Filters the projects   |
Pierre-Yves Chibon 47269d
    |               |          |               |   returned by their tags |
Pierre-Yves Chibon 47269d
    +---------------+----------+---------------+--------------------------+
Ryan Lerch 7c38e4
    | ``pattern``   | string   | Optional      | | Filters the projects   |
Ryan Lerch 7c38e4
    |               |          |               |   by the pattern string  |
Ryan Lerch 7c38e4
    +---------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 47269d
    | ``username``  | string   | Optional      | | Filters the projects   |
Pierre-Yves Chibon 47269d
    |               |          |               |   returned by the users  |
Pierre-Yves Chibon 47269d
    |               |          |               |   having commit rights   |
Pierre-Yves Chibon 47269d
    |               |          |               |   to it                  |
Pierre-Yves Chibon 47269d
    +---------------+----------+---------------+--------------------------+
Matt Prahl 7403a2
    | ``owner``     | string   | Optional      | | Filters the projects   |
Pierre-Yves Chibon 6dc7c9
    |               |          |               |   by ownership.          |
Pierre-Yves Chibon 6dc7c9
    |               |          |               |   If the argument is of  |
Pierre-Yves Chibon 6dc7c9
    |               |          |               |   the form  then |
Pierre-Yves Chibon 6dc7c9
    |               |          |               |   the project returned   |
Pierre-Yves Chibon 6dc7c9
    |               |          |               |   are the ones *not*     |
Pierre-Yves Chibon 6dc7c9
    |               |          |               |   owned by this user.    |
Matt Prahl 7403a2
    +---------------+----------+---------------+--------------------------+
Matt Prahl 6c3d54
    | ``namespace`` | string   | Optional      | | Filters the projects   |
Matt Prahl 6c3d54
    |               |          |               |   by namespace           |
Matt Prahl 6c3d54
    +---------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 47269d
    | ``fork``      | boolean  | Optional      | | Filters the projects   |
Pierre-Yves Chibon 47269d
    |               |          |               |   returned depending if  |
Pierre-Yves Chibon 47269d
    |               |          |               |   they are forks or not  |
Pierre-Yves Chibon 47269d
    +---------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 1ca90b
    | ``short``     | boolean  | Optional      | | Whether to return the  |
Pierre-Yves Chibon 1ca90b
    |               |          |               |   entrie project JSON    |
Pierre-Yves Chibon 1ca90b
    |               |          |               |   or just a sub-set      |
Pierre-Yves Chibon 1ca90b
    +---------------+----------+---------------+--------------------------+
Karsten Hopp a5d5f1
    | ``page``      | int      | Optional      | | Specifies which        |
Karsten Hopp a5d5f1
    |               |          |               |   page to return         |
Karsten Hopp a5d5f1
    |               |          |               |   (defaults to: 1)       |
Matt Prahl a01c25
    +---------------+----------+---------------+--------------------------+
Matt Prahl a01c25
    | ``per_page``  | int      | Optional      | | The number of projects |
Matt Prahl a01c25
    |               |          |               |   to return per page.    |
Matt Prahl a01c25
    |               |          |               |   The maximum is 100.    |
Matt Prahl a01c25
    +---------------+----------+---------------+--------------------------+
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
    Sample response
Pierre-Yves Chibon 47269d
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
    ::
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 47269d
        {
Matt Prahl a01c25
          "args": {
Matt Prahl a01c25
            "fork": null,
Matt Prahl a01c25
            "namespace": null,
Matt Prahl a01c25
            "owner": null,
Matt Prahl a01c25
            "page": 1,
Matt Prahl a01c25
            "pattern": null,
Matt Prahl a01c25
            "per_page": 2,
Matt Prahl a01c25
            "short": false,
Matt Prahl a01c25
            "tags": [],
Matt Prahl a01c25
            "username": null
Matt Prahl a01c25
          },
Matt Prahl a01c25
          "pagination": {
Matt Prahl a01c25
            "first": "http://127.0.0.1:5000/api/0/projects?per_page=2&page=1",
Matt Prahl a01c25
            "last": "http://127.0.0.1:5000/api/0/projects?per_page=2&page=500",
Matt Prahl a01c25
            "next": "http://127.0.0.1:5000/api/0/projects?per_page=2&page=2",
Matt Prahl a01c25
            "page": 1,
Matt Prahl a01c25
            "pages": 500,
Matt Prahl a01c25
            "per_page": 2,
Matt Prahl a01c25
            "prev": null
Matt Prahl a01c25
          },
Matt Prahl a01c25
          "projects": [
Matt Prahl a01c25
            {
Matt Prahl a01c25
              "access_groups": {
Matt Prahl a01c25
                "admin": [],
Matt Prahl a01c25
                "commit": [],
Matt Prahl a01c25
                "ticket": []
Matt Prahl a01c25
              },
Matt Prahl a01c25
              "access_users": {
Matt Prahl a01c25
                "admin": [],
Matt Prahl a01c25
                "commit": [],
Matt Prahl a01c25
                "owner": [
Matt Prahl a01c25
                  "mprahl"
Matt Prahl a01c25
                ],
Matt Prahl a01c25
                "ticket": []
Matt Prahl a01c25
              },
Matt Prahl a01c25
              "close_status": [],
Matt Prahl a01c25
              "custom_keys": [],
Matt Prahl a01c25
              "date_created": "1498841289",
Matt Prahl a01c25
              "description": "test1",
Matt Prahl a01c25
              "fullname": "test1",
Matt Prahl a01c25
              "id": 1,
Matt Prahl a01c25
              "milestones": {},
Matt Prahl a01c25
              "name": "test1",
Matt Prahl a01c25
              "namespace": null,
Matt Prahl a01c25
              "parent": null,
Matt Prahl a01c25
              "priorities": {},
Matt Prahl a01c25
              "tags": [],
Karsten Hopp 46ce34
              "url_path": "test1",
Matt Prahl a01c25
              "user": {
Matt Prahl a01c25
                "fullname": "Matt Prahl",
Matt Prahl a01c25
                "name": "mprahl"
Matt Prahl a01c25
              }
Matt Prahl a01c25
            },
Matt Prahl a01c25
            {
Matt Prahl a01c25
              "access_groups": {
Matt Prahl a01c25
                "admin": [],
Matt Prahl a01c25
                "commit": [],
Matt Prahl a01c25
                "ticket": []
Matt Prahl a01c25
              },
Matt Prahl a01c25
              "access_users": {
Matt Prahl a01c25
                "admin": [],
Matt Prahl a01c25
                "commit": [],
Matt Prahl a01c25
                "owner": [
Matt Prahl a01c25
                  "mprahl"
Matt Prahl a01c25
                ],
Matt Prahl a01c25
                "ticket": []
Matt Prahl a01c25
              },
Matt Prahl a01c25
              "close_status": [],
Matt Prahl a01c25
              "custom_keys": [],
Matt Prahl a01c25
              "date_created": "1499795310",
Matt Prahl a01c25
              "description": "test2",
Matt Prahl a01c25
              "fullname": "test2",
Matt Prahl a01c25
              "id": 2,
Matt Prahl a01c25
              "milestones": {},
Matt Prahl a01c25
              "name": "test2",
Matt Prahl a01c25
              "namespace": null,
Matt Prahl a01c25
              "parent": null,
Matt Prahl a01c25
              "priorities": {},
Matt Prahl a01c25
              "tags": [],
Karsten Hopp 46ce34
              "url_path": "test2",
Matt Prahl a01c25
              "user": {
Matt Prahl a01c25
                "fullname": "Matt Prahl",
Matt Prahl a01c25
                "name": "mprahl"
Matt Prahl a01c25
              }
Matt Prahl a01c25
            }
Matt Prahl a01c25
          ],
Matt Prahl a01c25
          "total_projects": 1000
Matt Prahl a01c25
        }
Pierre-Yves Chibon 47269d
    """
Pierre-Yves Chibon 9c2953
    tags = flask.request.values.getlist("tags")
Pierre-Yves Chibon 9c2953
    username = flask.request.values.get("username", None)
Pierre-Yves Chibon 9c2953
    fork = flask.request.values.get("fork", None)
Pierre-Yves Chibon 9c2953
    namespace = flask.request.values.get("namespace", None)
Pierre-Yves Chibon 9c2953
    owner = flask.request.values.get("owner", None)
Pierre-Yves Chibon 9c2953
    pattern = flask.request.values.get("pattern", None)
Pierre-Yves Chibon 9c2953
    short = pagure.utils.is_true(flask.request.values.get("short", False))
Pierre-Yves Chibon 47269d
Aurélien Bompard 619e2a
    if fork is not None:
Aurélien Bompard 619e2a
        fork = pagure.utils.is_true(fork)
Pierre-Yves Chibon 47269d
farhaanbukhsh 7ca1b1
    private = False
Pierre-Yves Chibon 9c2953
    if pagure.utils.authenticated() and username == flask.g.fas_user.username:
farhaanbukhsh 7ca1b1
        private = flask.g.fas_user.username
farhaanbukhsh 7ca1b1
Pierre-Yves Chibon 930073
    project_count = pagure.lib.query.search_projects(
Pierre-Yves Chibon 9c2953
        flask.g.session,
Pierre-Yves Chibon 9c2953
        username=username,
Pierre-Yves Chibon 9c2953
        fork=fork,
Pierre-Yves Chibon 9c2953
        tags=tags,
Pierre-Yves Chibon 9c2953
        pattern=pattern,
Pierre-Yves Chibon 9c2953
        private=private,
Pierre-Yves Chibon 9c2953
        namespace=namespace,
Pierre-Yves Chibon 9c2953
        owner=owner,
Pierre-Yves Chibon 9c2953
        count=True,
Pierre-Yves Chibon 9c2953
    )
Karsten Hopp a5d5f1
Pierre-Yves Chibon 90cfb8
    # Pagination code inspired by Flask-SQLAlchemy
Pierre-Yves Chibon 90cfb8
    page = get_page()
Pierre-Yves Chibon 90cfb8
    per_page = get_per_page()
Pierre-Yves Chibon 930073
    pagination_metadata = pagure.lib.query.get_pagination_metadata(
Pierre-Yves Chibon 9c2953
        flask.request, page, per_page, project_count
Pierre-Yves Chibon 9c2953
    )
Karsten Hopp a5d5f1
    query_start = (page - 1) * per_page
Karsten Hopp a5d5f1
    query_limit = per_page
Matt Prahl a01c25
Pierre-Yves Chibon 930073
    projects = pagure.lib.query.search_projects(
Pierre-Yves Chibon 9c2953
        flask.g.session,
Pierre-Yves Chibon 9c2953
        username=username,
Pierre-Yves Chibon 9c2953
        fork=fork,
Pierre-Yves Chibon 9c2953
        tags=tags,
Pierre-Yves Chibon 9c2953
        pattern=pattern,
Pierre-Yves Chibon 9c2953
        private=private,
Pierre-Yves Chibon 9c2953
        namespace=namespace,
Pierre-Yves Chibon 9c2953
        owner=owner,
Pierre-Yves Chibon 9c2953
        limit=query_limit,
Pierre-Yves Chibon 9c2953
        start=query_start,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 47269d
Vivek Anand dfe82f
    # prepare the output json
Vivek Anand dfe82f
    jsonout = {
Pierre-Yves Chibon 9c2953
        "total_projects": project_count,
Pierre-Yves Chibon 9c2953
        "projects": projects,
Pierre-Yves Chibon 9c2953
        "args": {
Pierre-Yves Chibon 9c2953
            "tags": tags,
Pierre-Yves Chibon 9c2953
            "username": username,
Pierre-Yves Chibon 9c2953
            "fork": fork,
Pierre-Yves Chibon 9c2953
            "pattern": pattern,
Pierre-Yves Chibon 9c2953
            "namespace": namespace,
Pierre-Yves Chibon 9c2953
            "owner": owner,
Pierre-Yves Chibon 9c2953
            "short": short,
Pierre-Yves Chibon 9c2953
        },
Vivek Anand dfe82f
    }
Pierre-Yves Chibon 47269d
Pierre-Yves Chibon 1ca90b
    if not short:
Pierre-Yves Chibon 1ca90b
        projects = [p.to_json(api=True, public=True) for p in projects]
Pierre-Yves Chibon 1ca90b
    else:
Pierre-Yves Chibon 1ca90b
        projects = [
Pierre-Yves Chibon 1ca90b
            {
Pierre-Yves Chibon 9c2953
                "name": p.name,
Pierre-Yves Chibon 9c2953
                "namespace": p.namespace,
Pierre-Yves Chibon 9c2953
                "fullname": p.fullname.replace("forks/", "fork/", 1)
Pierre-Yves Chibon 9c2953
                if p.fullname.startswith("forks/")
Pierre-Yves Chibon 9c2953
                else p.fullname,
Pierre-Yves Chibon 9c2953
                "description": p.description,
Pierre-Yves Chibon 1ca90b
            }
Pierre-Yves Chibon 1ca90b
            for p in projects
Pierre-Yves Chibon 1ca90b
        ]
Pierre-Yves Chibon 1ca90b
Pierre-Yves Chibon 9c2953
    jsonout["projects"] = projects
Matt Prahl a01c25
    if pagination_metadata:
Pierre-Yves Chibon 9c2953
        jsonout["args"]["page"] = page
Pierre-Yves Chibon 9c2953
        jsonout["args"]["per_page"] = per_page
Pierre-Yves Chibon 9c2953
        jsonout["pagination"] = pagination_metadata
Matt Prahl a01c25
    return flask.jsonify(jsonout)
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9c2953
@API.route("/<repo>")</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>")</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>")</repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>")</repo></namespace></username>
Pierre-Yves Chibon b42ba1
@api_method
Pierre-Yves Chibon b42ba1
def api_project(repo, username=None, namespace=None):
Pierre-Yves Chibon b42ba1
    """
Pierre-Yves Chibon b42ba1
    Project information
Pierre-Yves Chibon b42ba1
    -------------------
Pierre-Yves Chibon b42ba1
    Return information about a specific project
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
    ::
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
        GET /api/0/<repo></repo>
Pierre-Yves Chibon b42ba1
        GET /api/0/<namespace>/<repo></repo></namespace>
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
    ::
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
        GET /api/0/fork/<username>/<repo></repo></username>
Pierre-Yves Chibon b42ba1
        GET /api/0/fork/<username>/<namespace>/<repo></repo></namespace></username>
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
    Sample response
Pierre-Yves Chibon b42ba1
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
    ::
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
        {
Pierre-Yves Chibon b6f024
          "access_groups": {
Pierre-Yves Chibon b6f024
            "admin": [],
Pierre-Yves Chibon b6f024
            "commit": [],
Pierre-Yves Chibon b6f024
            "ticket": []
Pierre-Yves Chibon b6f024
          },
Pierre-Yves Chibon b6f024
          "access_users": {
Pierre-Yves Chibon b6f024
            "admin": [
Pierre-Yves Chibon b6f024
              "ryanlerch"
Pierre-Yves Chibon b6f024
            ],
Pierre-Yves Chibon b6f024
            "commit": [
Pierre-Yves Chibon b6f024
              "puiterwijk"
Pierre-Yves Chibon b6f024
            ],
Pierre-Yves Chibon b6f024
            "owner": [
Pierre-Yves Chibon b6f024
              "pingou"
Pierre-Yves Chibon b6f024
            ],
Pierre-Yves Chibon b6f024
            "ticket": [
Pierre-Yves Chibon b6f024
              "vivekanand1101",
Pierre-Yves Chibon b6f024
              "mprahl",
Pierre-Yves Chibon b6f024
              "jcline",
Pierre-Yves Chibon b6f024
              "lslebodn",
Pierre-Yves Chibon b6f024
              "cverna",
Pierre-Yves Chibon b6f024
              "farhaan"
Pierre-Yves Chibon b6f024
            ]
Pierre-Yves Chibon b6f024
          },
Pierre-Yves Chibon b6f024
          "close_status": [
Pierre-Yves Chibon b6f024
            "Invalid",
Pierre-Yves Chibon b6f024
            "Insufficient data",
Pierre-Yves Chibon b6f024
            "Fixed",
Pierre-Yves Chibon b6f024
            "Duplicate"
Pierre-Yves Chibon b6f024
          ],
Pierre-Yves Chibon b6f024
          "custom_keys": [],
Pierre-Yves Chibon b6f024
          "date_created": "1431549490",
Pierre-Yves Chibon b6f024
          "date_modified": "1431549490",
Pierre-Yves Chibon b6f024
          "description": "A git centered forge",
Pierre-Yves Chibon b6f024
          "fullname": "pagure",
Pierre-Yves Chibon b6f024
          "id": 10,
Pierre-Yves Chibon b6f024
          "milestones": {},
Pierre-Yves Chibon b6f024
          "name": "pagure",
Pierre-Yves Chibon b6f024
          "namespace": null,
Pierre-Yves Chibon b6f024
          "parent": null,
Pierre-Yves Chibon b6f024
          "priorities": {},
Pierre-Yves Chibon b6f024
          "tags": [
Pierre-Yves Chibon b6f024
            "pagure",
Pierre-Yves Chibon b6f024
            "fedmsg"
Pierre-Yves Chibon b6f024
          ],
Pierre-Yves Chibon b6f024
          "user": {
Pierre-Yves Chibon b6f024
            "fullname": "Pierre-YvesChibon",
Pierre-Yves Chibon b6f024
            "name": "pingou"
Pierre-Yves Chibon b6f024
          }
Pierre-Yves Chibon b42ba1
        }
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
    """
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Pierre-Yves Chibon b42ba1
Aurélien Bompard 619e2a
    expand_group = pagure.utils.is_true(
Pierre-Yves Chibon 9c2953
        flask.request.values.get("expand_group", False)
Aurélien Bompard 619e2a
    )
Pierre-Yves Chibon fd3701
Pierre-Yves Chibon fd3701
    output = repo.to_json(api=True, public=True)
Pierre-Yves Chibon fd3701
Pierre-Yves Chibon fd3701
    if expand_group:
Pierre-Yves Chibon fd3701
        group_details = {}
Pierre-Yves Chibon fd3701
        for grp in repo.projects_groups:
Pierre-Yves Chibon fd3701
            group_details[grp.group.group_name] = [
Pierre-Yves Chibon 9c2953
                user.username for user in grp.group.users
Pierre-Yves Chibon 9c2953
            ]
Pierre-Yves Chibon 9c2953
        output["group_details"] = group_details
Pierre-Yves Chibon fd3701
Pierre-Yves Chibon fd3701
    jsonout = flask.jsonify(output)
Pierre-Yves Chibon b42ba1
    return jsonout
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon b42ba1
Pierre-Yves Chibon 9c2953
@API.route("/new/", methods=["POST"])
Pierre-Yves Chibon 9c2953
@API.route("/new", methods=["POST"])
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["create_project"])
Pierre-Yves Chibon 9f9b1e
@api_method
Pierre-Yves Chibon 9f9b1e
def api_new_project():
Pierre-Yves Chibon 9f9b1e
    """
Pierre-Yves Chibon 9f9b1e
    Create a new project
Pierre-Yves Chibon 9f9b1e
    --------------------
Pierre-Yves Chibon 9f9b1e
    Create a new project on this pagure instance.
Pierre-Yves Chibon 9f9b1e
Patrick Uiterwijk bb08fb
    This is an asynchronous call.
Patrick Uiterwijk bb08fb
Pierre-Yves Chibon 9f9b1e
    ::
Pierre-Yves Chibon 9f9b1e
Matt Prahl e7ba58
        POST /api/0/new
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9f9b1e
    Input
Pierre-Yves Chibon 9f9b1e
    ^^^^^
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | Key                        | Type    | Optionality  | Description               |
Pierre-Yves Chibon bdc553
    +============================+=========+==============+===========================+
Pierre-Yves Chibon bdc553
    | ``name``                   | string  | Mandatory    | | The name of the new     |
Pierre-Yves Chibon bdc553
    |                            |         |              |   project.                |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``description``            | string  | Mandatory    | | A short description of  |
Pierre-Yves Chibon bdc553
    |                            |         |              |   the new project.        |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``namespace``              | string  | Optional     | | The namespace of the    |
Pierre-Yves Chibon bdc553
    |                            |         |              |   project to fork.        |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``url``                    | string  | Optional     | | An url providing more   |
Pierre-Yves Chibon bdc553
    |                            |         |              |   information about the   |
Pierre-Yves Chibon bdc553
    |                            |         |              |   project.                |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``avatar_email``           | string  | Optional     | | An email address for the|
Pierre-Yves Chibon bdc553
    |                            |         |              |   avatar of the project.  |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``create_readme``          | boolean | Optional     | | A boolean to specify if |
Pierre-Yves Chibon bdc553
    |                            |         |              |   there should be a readme|
Pierre-Yves Chibon bdc553
    |                            |         |              |   added to the project on |
Pierre-Yves Chibon bdc553
    |                            |         |              |   creation.               |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``private``                | boolean | Optional     | | A boolean to specify if |
Pierre-Yves Chibon bdc553
    |                            |         |              |   the project to create   |
Pierre-Yves Chibon bdc553
    |                            |         |              |   is private.             |
Pierre-Yves Chibon bdc553
    |                            |         |              |   Note: not all pagure    |
Pierre-Yves Chibon bdc553
    |                            |         |              |   instance support private|
Pierre-Yves Chibon bdc553
    |                            |         |              |   projects, confirm this  |
Pierre-Yves Chibon bdc553
    |                            |         |              |   with your administrators|
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``ignore_existing_repos``  | boolean | Optional     | | Only available to admins|
Pierre-Yves Chibon bdc553
    |                            |         |              |   this option allows them |
Pierre-Yves Chibon bdc553
    |                            |         |              |   to make project creation|
Pierre-Yves Chibon bdc553
    |                            |         |              |   pass even if there is   |
Pierre-Yves Chibon bdc553
    |                            |         |              |   already a coresopnding  |
Pierre-Yves Chibon bdc553
    |                            |         |              |   git repository on disk  |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``repospanner_region``     | boolean | Optional     | | Only available to admins|
Pierre-Yves Chibon bdc553
    |                            |         |              |   this option allows them |
Pierre-Yves Chibon bdc553
    |                            |         |              |   to override the default |
Pierre-Yves Chibon bdc553
    |                            |         |              |   respoSpanner region     |
Pierre-Yves Chibon bdc553
    |                            |         |              |   configured              |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon bdc553
    | ``wait``                   | boolean | Optional     | | A boolean to specify if |
Pierre-Yves Chibon bdc553
    |                            |         |              |   this API call should    |
Pierre-Yves Chibon bdc553
    |                            |         |              |   return a taskid or if it|
Pierre-Yves Chibon bdc553
    |                            |         |              |   should wait for the task|
Pierre-Yves Chibon bdc553
    |                            |         |              |   to finish.              |
Pierre-Yves Chibon bdc553
    +----------------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9f9b1e
    Sample response
Pierre-Yves Chibon 9f9b1e
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9f9b1e
    ::
Pierre-Yves Chibon 9f9b1e
Patrick Uiterwijk 0281ff
        wait=False:
Pierre-Yves Chibon 9f9b1e
        {
Patrick Uiterwijk bb08fb
          'message': 'Project creation queued',
Patrick Uiterwijk bb08fb
          'taskid': '123-abcd'
Pierre-Yves Chibon 9f9b1e
        }
Pierre-Yves Chibon 9f9b1e
Patrick Uiterwijk 0281ff
        wait=True:
Patrick Uiterwijk 0281ff
        {
Patrick Uiterwijk 0281ff
          'message': 'Project creation queued'
Patrick Uiterwijk 0281ff
        }
Patrick Uiterwijk 0281ff
Pierre-Yves Chibon bdc553
    """  # noqa
Pierre-Yves Chibon 930073
    user = pagure.lib.query.search_user(
Pierre-Yves Chibon 9c2953
        flask.g.session, username=flask.g.fas_user.username
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9f9b1e
    output = {}
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9c2953
    if not pagure_config.get("ENABLE_NEW_PROJECTS", True):
Pierre-Yves Chibon 9f9b1e
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            404, error_code=APIERROR.ENEWPROJECTDISABLED
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9c2953
    namespaces = pagure_config["ALLOWED_PREFIX"][:]
Pierre-Yves Chibon a5c678
    if user:
Pierre-Yves Chibon a5c678
        namespaces.extend([grp for grp in user.groups])
Pierre-Yves Chibon a5c678
Pierre-Yves Chibon 9c2953
    form = pagure.forms.ProjectForm(namespaces=namespaces, csrf_enabled=False)
Pierre-Yves Chibon 9f9b1e
    if form.validate_on_submit():
Pierre-Yves Chibon 9f9b1e
        name = form.name.data
Pierre-Yves Chibon 9f9b1e
        description = form.description.data
Pierre-Yves Chibon fe2a31
        namespace = form.namespace.data
Pierre-Yves Chibon 9f9b1e
        url = form.url.data
Pierre-Yves Chibon 9f9b1e
        avatar_email = form.avatar_email.data
Pierre-Yves Chibon 9f9b1e
        create_readme = form.create_readme.data
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 3175b1
        if namespace:
Pierre-Yves Chibon 3175b1
            namespace = namespace.strip()
Pierre-Yves Chibon 3175b1
Pierre-Yves Chibon 3a7944
        private = False
Pierre-Yves Chibon 9c2953
        if pagure_config.get("PRIVATE_PROJECTS", False):
Pierre-Yves Chibon 3a7944
            private = form.private.data
Patrick Uiterwijk 3f97f6
        if form.repospanner_region:
Patrick Uiterwijk 3f97f6
            repospanner_region = form.repospanner_region.data
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            repospanner_region = None
Pierre-Yves Chibon 7cc66f
        if form.ignore_existing_repos:
Pierre-Yves Chibon 7cc66f
            ignore_existing_repos = form.ignore_existing_repos.data
Pierre-Yves Chibon 7cc66f
        else:
Pierre-Yves Chibon 7cc66f
            ignore_existing_repos = False
Pierre-Yves Chibon 3a7944
Pierre-Yves Chibon 9f9b1e
        try:
Pierre-Yves Chibon 930073
            task = pagure.lib.query.new_project(
Pierre-Yves Chibon b130e5
                flask.g.session,
Pierre-Yves Chibon 9f9b1e
                name=name,
Pierre-Yves Chibon 34ece4
                namespace=namespace,
Patrick Uiterwijk 3f97f6
                repospanner_region=repospanner_region,
Pierre-Yves Chibon 7cc66f
                ignore_existing_repo=ignore_existing_repos,
Pierre-Yves Chibon 9f9b1e
                description=description,
Pierre-Yves Chibon 3a7944
                private=private,
Pierre-Yves Chibon 9f9b1e
                url=url,
Pierre-Yves Chibon 9f9b1e
                avatar_email=avatar_email,
Pierre-Yves Chibon 9f9b1e
                user=flask.g.fas_user.username,
Pierre-Yves Chibon 9c2953
                blacklist=pagure_config["BLACKLISTED_PROJECTS"],
Pierre-Yves Chibon 9c2953
                allowed_prefix=pagure_config["ALLOWED_PREFIX"],
Pierre-Yves Chibon 9f9b1e
                add_readme=create_readme,
Pierre-Yves Chibon 9f9b1e
                userobj=user,
Pierre-Yves Chibon b130e5
                prevent_40_chars=pagure_config.get(
Pierre-Yves Chibon 9c2953
                    "OLD_VIEW_COMMIT_ENABLED", False
Pierre-Yves Chibon 9c2953
                ),
Pierre-Yves Chibon 9c2953
                user_ns=pagure_config.get("USER_NAMESPACE", False),
Pierre-Yves Chibon 9f9b1e
            )
Pierre-Yves Chibon b130e5
            flask.g.session.commit()
Pierre-Yves Chibon 9c2953
            output = {"message": "Project creation queued", "taskid": task.id}
Patrick Uiterwijk 0281ff
Pierre-Yves Chibon 9c2953
            if get_request_data().get("wait", True):
Aurélien Bompard a7f281
                result = task.get()
Pierre-Yves Chibon 930073
                project = pagure.lib.query._get_project(
Pierre-Yves Chibon 9c2953
                    flask.g.session,
Pierre-Yves Chibon 9c2953
                    name=result["repo"],
Pierre-Yves Chibon 9c2953
                    namespace=result["namespace"],
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 9c2953
                output = {"message": 'Project "%s" created' % project.fullname}
Pierre-Yves Chibon 9f9b1e
        except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon 9f9b1e
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 9f9b1e
        except SQLAlchemyError as err:  # pragma: no cover
Pierre-Yves Chibon b130e5
            _log.exception(err)
Pierre-Yves Chibon b130e5
            flask.g.session.rollback()
Pierre-Yves Chibon 9f9b1e
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Pierre-Yves Chibon 9f9b1e
    else:
Pierre-Yves Chibon f7fcaa
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9f9b1e
Pierre-Yves Chibon 9f9b1e
    jsonout = flask.jsonify(output)
Pierre-Yves Chibon 9f9b1e
    return jsonout
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon 9c2953
@API.route("/<repo>", methods=["PATCH"])</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>", methods=["PATCH"])</repo></namespace>
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["modify_project"])
Matt Prahl 30f8b1
@api_method
Matt Prahl 30f8b1
def api_modify_project(repo, namespace=None):
Matt Prahl 30f8b1
    """
Matt Prahl 30f8b1
    Modify a project
Matt Prahl 30f8b1
    ----------------
Matt Prahl 30f8b1
    Modify an existing project on this Pagure instance.
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    ::
Matt Prahl 30f8b1
Matt Prahl 30f8b1
        PATCH /api/0/<repo></repo>
Matt Prahl 30f8b1
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    Input
Matt Prahl 30f8b1
    ^^^^^
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    +------------------+---------+--------------+---------------------------+
Matt Prahl 30f8b1
    | Key              | Type    | Optionality  | Description               |
Matt Prahl 30f8b1
    +==================+=========+==============+===========================+
Matt Prahl 30f8b1
    | ``main_admin``   | string  | Mandatory    | | The new main admin of   |
Matt Prahl 30f8b1
    |                  |         |              |   the project.            |
Matt Prahl 30f8b1
    +------------------+---------+--------------+---------------------------+
mprahl 8f40ee
    | ``retain_access``| string  | Optional     | | The old main admin      |
mprahl 8f40ee
    |                  |         |              |   retains access on the   |
mprahl 8f40ee
    |                  |         |              |   project when giving the |
mprahl 8f40ee
    |                  |         |              |   project. Defaults to    |
mprahl 8f40ee
    |                  |         |              |   ``False``.              |
mprahl 8f40ee
    +------------------+---------+--------------+---------------------------+
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    Sample response
Matt Prahl 30f8b1
    ^^^^^^^^^^^^^^^
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    ::
Matt Prahl 30f8b1
Matt Prahl 30f8b1
        {
Matt Prahl 30f8b1
          "access_groups": {
Matt Prahl 30f8b1
            "admin": [],
Matt Prahl 30f8b1
            "commit": [],
Matt Prahl 30f8b1
            "ticket": []
Matt Prahl 30f8b1
          },
Matt Prahl 30f8b1
          "access_users": {
Matt Prahl 30f8b1
            "admin": [],
Matt Prahl 30f8b1
            "commit": [],
Matt Prahl 30f8b1
            "owner": [
Matt Prahl 30f8b1
              "testuser1"
Matt Prahl 30f8b1
            ],
Matt Prahl 30f8b1
            "ticket": []
Matt Prahl 30f8b1
          },
Matt Prahl 30f8b1
          "close_status": [],
Matt Prahl 30f8b1
          "custom_keys": [],
Matt Prahl 30f8b1
          "date_created": "1496326387",
Matt Prahl 30f8b1
          "description": "Test",
Matt Prahl 30f8b1
          "fullname": "test-project2",
Matt Prahl 30f8b1
          "id": 2,
Matt Prahl 30f8b1
          "milestones": {},
Matt Prahl 30f8b1
          "name": "test-project2",
Matt Prahl 30f8b1
          "namespace": null,
Matt Prahl 30f8b1
          "parent": null,
Matt Prahl 30f8b1
          "priorities": {},
Matt Prahl 30f8b1
          "tags": [],
Matt Prahl 30f8b1
          "user": {
Matt Prahl 30f8b1
            "default_email": "testuser1@domain.local",
Matt Prahl 30f8b1
            "emails": [],
Matt Prahl 30f8b1
            "fullname": "Test User1",
Matt Prahl 30f8b1
            "name": "testuser1"
Matt Prahl 30f8b1
          }
Matt Prahl 30f8b1
        }
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    """
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, namespace=namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Pierre-Yves Chibon e99858
Pierre-Yves Chibon b130e5
    is_site_admin = pagure.utils.is_admin()
Pierre-Yves Chibon 9c2953
    admins = [u.username for u in project.get_project_users("admin")]
Matt Prahl ab0f46
    # Only allow the main admin, the admins of the project, and Pagure site
Matt Prahl ab0f46
    # admins to modify projects, even if the user has the right ACLs on their
Matt Prahl ab0f46
    # token
Pierre-Yves Chibon 9c2953
    if (
Pierre-Yves Chibon 9c2953
        flask.g.fas_user.username not in admins
Pierre-Yves Chibon 9c2953
        and flask.g.fas_user.username != project.user.username
Pierre-Yves Chibon 9c2953
        and not is_site_admin
Pierre-Yves Chibon 9c2953
    ):
Matt Prahl 30f8b1
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            401, error_code=APIERROR.EMODIFYPROJECTNOTALLOWED
Pierre-Yves Chibon 9c2953
        )
Matt Prahl 30f8b1
Pierre-Yves Chibon 9c2953
    valid_keys = ["main_admin", "retain_access"]
mprahl 0f9ac5
    # Check if it's JSON or form data
Pierre-Yves Chibon 9c2953
    if flask.request.headers.get("Content-Type") == "application/json":
mprahl 0f9ac5
        # Set force to True to ignore the mimetype. Set silent so that None is
mprahl 0f9ac5
        # returned if it's invalid JSON.
mprahl 0f9ac5
        args = flask.request.get_json(force=True, silent=True) or {}
Pierre-Yves Chibon 9c2953
        retain_access = args.get("retain_access", False)
mprahl 0f9ac5
    else:
Slavek Kabrda 727932
        args = get_request_data()
Pierre-Yves Chibon 9c2953
        retain_access = args.get("retain_access", "").lower() in ["true", "1"]
mprahl 0f9ac5
mprahl 0f9ac5
    if not args:
Matt Prahl 30f8b1
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    # Check to make sure there aren't parameters we don't support
mprahl 0f9ac5
    for key in args.keys():
Matt Prahl 30f8b1
        if key not in valid_keys:
Matt Prahl 30f8b1
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                400, error_code=APIERROR.EINVALIDREQ
Pierre-Yves Chibon 9c2953
            )
Matt Prahl 30f8b1
Pierre-Yves Chibon 9c2953
    if "main_admin" in args:
Pierre-Yves Chibon 9c2953
        if (
Pierre-Yves Chibon 9c2953
            flask.g.fas_user.username != project.user.username
Pierre-Yves Chibon 9c2953
            and not is_site_admin
Pierre-Yves Chibon 9c2953
        ):
Matt Prahl 30f8b1
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                401, error_code=APIERROR.ENOTMAINADMIN
Pierre-Yves Chibon 9c2953
            )
Matt Prahl 30f8b1
        # If the main_admin is already set correctly, don't do anything
Matt Prahl 58c0b0
        if flask.g.fas_user.username == project.user:
Matt Prahl 30f8b1
            return flask.jsonify(project.to_json(public=False, api=True))
Matt Prahl 30f8b1
Matt Prahl 30f8b1
        try:
Pierre-Yves Chibon 930073
            new_main_admin = pagure.lib.query.get_user(
Pierre-Yves Chibon 9c2953
                flask.g.session, args["main_admin"]
Pierre-Yves Chibon 9c2953
            )
Matt Prahl 30f8b1
        except pagure.exceptions.PagureException:
Matt Prahl 30f8b1
            raise pagure.exceptions.APIError(400, error_code=APIERROR.ENOUSER)
Matt Prahl 30f8b1
mprahl 8f40ee
        old_main_admin = project.user.user
Pierre-Yves Chibon 930073
        pagure.lib.query.set_project_owner(
Pierre-Yves Chibon 930073
            flask.g.session, project, new_main_admin
Pierre-Yves Chibon 930073
        )
mprahl 8f40ee
        if retain_access and flask.g.fas_user.username == old_main_admin:
Pierre-Yves Chibon 930073
            pagure.lib.query.add_user_to_project(
Pierre-Yves Chibon 9c2953
                flask.g.session,
Pierre-Yves Chibon 9c2953
                project,
Pierre-Yves Chibon 9c2953
                new_user=flask.g.fas_user.username,
Pierre-Yves Chibon 9c2953
                user=flask.g.fas_user.username,
Pierre-Yves Chibon 9c2953
            )
Matt Prahl 30f8b1
Matt Prahl 30f8b1
    try:
Pierre-Yves Chibon b130e5
        flask.g.session.commit()
Matt Prahl 30f8b1
    except SQLAlchemyError:  # pragma: no cover
Pierre-Yves Chibon b130e5
        flask.g.session.rollback()
Pierre-Yves Chibon 9c2953
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Matt Prahl 30f8b1
Pierre-Yves Chibon fd97af
    pagure.lib.git.generate_gitolite_acls(project=project)
Pierre-Yves Chibon fd97af
Matt Prahl 30f8b1
    return flask.jsonify(project.to_json(public=False, api=True))
Matt Prahl 30f8b1
Matt Prahl 30f8b1
Pierre-Yves Chibon 9c2953
@API.route("/fork/", methods=["POST"])
Pierre-Yves Chibon 9c2953
@API.route("/fork", methods=["POST"])
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["fork_project"])
Pierre-Yves Chibon dc21df
@api_method
Pierre-Yves Chibon dc21df
def api_fork_project():
Pierre-Yves Chibon dc21df
    """
Pierre-Yves Chibon dc21df
    Fork a project
Pierre-Yves Chibon dc21df
    --------------------
Pierre-Yves Chibon dc21df
    Fork a project on this pagure instance.
Pierre-Yves Chibon dc21df
Patrick Uiterwijk bb08fb
    This is an asynchronous call.
Patrick Uiterwijk bb08fb
Pierre-Yves Chibon dc21df
    ::
Pierre-Yves Chibon dc21df
ishcherb 720ff5
        POST /api/0/fork
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
    Input
Pierre-Yves Chibon dc21df
    ^^^^^
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon dc21df
    | Key              | Type    | Optionality  | Description               |
Pierre-Yves Chibon dc21df
    +==================+=========+==============+===========================+
Pierre-Yves Chibon dc21df
    | ``repo``         | string  | Mandatory    | | The name of the project |
Pierre-Yves Chibon dc21df
    |                  |         |              |   to fork.                |
Pierre-Yves Chibon dc21df
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon 34ece4
    | ``namespace``    | string  | Optional     | | The namespace of the    |
Pierre-Yves Chibon 34ece4
    |                  |         |              |   project to fork.        |
Pierre-Yves Chibon 34ece4
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon dc21df
    | ``username``     | string  | Optional     | | The username of the user|
Pierre-Yves Chibon dc21df
    |                  |         |              |   of the fork.            |
Pierre-Yves Chibon dc21df
    +------------------+---------+--------------+---------------------------+
Patrick Uiterwijk 0281ff
    | ``wait``         | boolean | Optional     | | A boolean to specify if |
Patrick Uiterwijk 0281ff
    |                  |         |              |   this API call should    |
Patrick Uiterwijk 0281ff
    |                  |         |              |   return a taskid or if it|
Patrick Uiterwijk 0281ff
    |                  |         |              |   should wait for the task|
Patrick Uiterwijk 0281ff
    |                  |         |              |   to finish.              |
Patrick Uiterwijk 0281ff
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
    Sample response
Pierre-Yves Chibon dc21df
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
    ::
Pierre-Yves Chibon dc21df
Patrick Uiterwijk 0281ff
        wait=False:
Pierre-Yves Chibon dc21df
        {
Patrick Uiterwijk bb08fb
          "message": "Project forking queued",
Patrick Uiterwijk bb08fb
          "taskid": "123-abcd"
Pierre-Yves Chibon dc21df
        }
Pierre-Yves Chibon dc21df
Patrick Uiterwijk 0281ff
        wait=True:
Patrick Uiterwijk 0281ff
        {
Patrick Uiterwijk 0281ff
          "message": 'Repo "test" cloned to "pingou/test"
Patrick Uiterwijk 0281ff
        }
Patrick Uiterwijk 0281ff
Pierre-Yves Chibon dc21df
    """
Pierre-Yves Chibon dc21df
    output = {}
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon 5c561c
    form = pagure.forms.ForkRepoForm(csrf_enabled=False)
Pierre-Yves Chibon dc21df
    if form.validate_on_submit():
Pierre-Yves Chibon dc21df
        repo = form.repo.data
Pierre-Yves Chibon dc21df
        username = form.username.data or None
Pierre-Yves Chibon 096375
        namespace = form.namespace.data.strip() or None
Pierre-Yves Chibon dc21df
Patrick Uiterwijk eabc8e
        repo = get_authorized_api_project(
Pierre-Yves Chibon 9c2953
            flask.g.session, repo, user=username, namespace=namespace
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon dc21df
        if repo is None:
Pierre-Yves Chibon dc21df
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                404, error_code=APIERROR.ENOPROJECT
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
        try:
Pierre-Yves Chibon 930073
            task = pagure.lib.query.fork_project(
Patrick Uiterwijk 3f97f6
                flask.g.session, user=flask.g.fas_user.username, repo=repo
Pierre-Yves Chibon dc21df
            )
Pierre-Yves Chibon b130e5
            flask.g.session.commit()
Pierre-Yves Chibon 9c2953
            output = {"message": "Project forking queued", "taskid": task.id}
Patrick Uiterwijk 0281ff
Pierre-Yves Chibon 9c2953
            if get_request_data().get("wait", True):
Aurélien Bompard a7f281
                task.get()
Pierre-Yves Chibon 9c2953
                output = {
Pierre-Yves Chibon 9c2953
                    "message": 'Repo "%s" cloned to "%s/%s"'
Pierre-Yves Chibon 9c2953
                    % (repo.fullname, flask.g.fas_user.username, repo.fullname)
Pierre-Yves Chibon 9c2953
                }
Pierre-Yves Chibon dc21df
        except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon dc21df
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon dc21df
        except SQLAlchemyError as err:  # pragma: no cover
Pierre-Yves Chibon b130e5
            _log.exception(err)
Pierre-Yves Chibon b130e5
            flask.g.session.rollback()
Pierre-Yves Chibon 9c2953
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Pierre-Yves Chibon dc21df
    else:
Pierre-Yves Chibon dc21df
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon dc21df
Pierre-Yves Chibon dc21df
    jsonout = flask.jsonify(output)
Pierre-Yves Chibon dc21df
    return jsonout
Matt Prahl 489ad1
Matt Prahl 489ad1
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/git/generateacls", methods=["POST"])</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/git/generateacls", methods=["POST"])</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/git/generateacls", methods=["POST"])</repo></username>
Pierre-Yves Chibon 9c2953
@API.route(
Pierre-Yves Chibon 9c2953
    "/fork/<username>/<namespace>/<repo>/git/generateacls", methods=["POST"]</repo></namespace></username>
Pierre-Yves Chibon 9c2953
)
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["generate_acls_project"])
Matt Prahl 489ad1
@api_method
Matt Prahl 489ad1
def api_generate_acls(repo, username=None, namespace=None):
Matt Prahl 489ad1
    """
Matt Prahl 489ad1
    Generate Gitolite ACLs on a project
Matt Prahl 489ad1
    -----------------------------------
Matt Prahl 489ad1
    Generate Gitolite ACLs on a project. This is restricted to Pagure admins.
Matt Prahl 489ad1
Matt Prahl 489ad1
    This is an asynchronous call.
Matt Prahl 489ad1
Matt Prahl 489ad1
    ::
Matt Prahl 489ad1
Matt Prahl 489ad1
        POST /api/0/rpms/python-requests/git/generateacls
Matt Prahl 489ad1
Matt Prahl 489ad1
Matt Prahl 489ad1
    Input
Matt Prahl 489ad1
    ^^^^^
Matt Prahl 489ad1
Matt Prahl 489ad1
    +------------------+---------+--------------+---------------------------+
Matt Prahl 489ad1
    | Key              | Type    | Optionality  | Description               |
Matt Prahl 489ad1
    +==================+=========+==============+===========================+
Matt Prahl 489ad1
    | ``wait``         | boolean | Optional     | | A boolean to specify if |
Matt Prahl 489ad1
    |                  |         |              |   this API call should    |
Matt Prahl 489ad1
    |                  |         |              |   return a taskid or if it|
Matt Prahl 489ad1
    |                  |         |              |   should wait for the task|
Matt Prahl 489ad1
    |                  |         |              |   to finish.              |
Matt Prahl 489ad1
    +------------------+---------+--------------+---------------------------+
Matt Prahl 489ad1
Matt Prahl 489ad1
Matt Prahl 489ad1
    Sample response
Matt Prahl 489ad1
    ^^^^^^^^^^^^^^^
Matt Prahl 489ad1
Matt Prahl 489ad1
    ::
Matt Prahl 489ad1
Matt Prahl 489ad1
        wait=False:
Matt Prahl 489ad1
        {
Matt Prahl 489ad1
          'message': 'Project ACL generation queued',
Matt Prahl 489ad1
          'taskid': '123-abcd'
Matt Prahl 489ad1
        }
Matt Prahl 489ad1
Matt Prahl 489ad1
        wait=True:
Matt Prahl 489ad1
        {
Matt Prahl 489ad1
          'message': 'Project ACLs generated'
Matt Prahl 489ad1
        }
Matt Prahl 489ad1
Matt Prahl 489ad1
    """
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Pierre-Yves Chibon e99858
mprahl 0f9ac5
    # Check if it's JSON or form data
Pierre-Yves Chibon 9c2953
    if flask.request.headers.get("Content-Type") == "application/json":
mprahl 0f9ac5
        # Set force to True to ignore the mimetype. Set silent so that None is
mprahl 0f9ac5
        # returned if it's invalid JSON.
mprahl 0f9ac5
        json = flask.request.get_json(force=True, silent=True) or {}
Pierre-Yves Chibon 9c2953
        wait = json.get("wait", False)
mprahl 0f9ac5
    else:
Pierre-Yves Chibon 9c2953
        wait = pagure.utils.is_true(get_request_data().get("wait"))
Matt Prahl 489ad1
Matt Prahl 489ad1
    try:
Pierre-Yves Chibon 9c2953
        task = pagure.lib.git.generate_gitolite_acls(project=project)
Matt Prahl 489ad1
Matt Prahl 489ad1
        if wait:
Aurélien Bompard a7f281
            task.get()
Pierre-Yves Chibon 9c2953
            output = {"message": "Project ACLs generated"}
Matt Prahl 489ad1
        else:
Pierre-Yves Chibon 9c2953
            output = {
Pierre-Yves Chibon 9c2953
                "message": "Project ACL generation queued",
Pierre-Yves Chibon 9c2953
                "taskid": task.id,
Pierre-Yves Chibon 9c2953
            }
Matt Prahl 489ad1
    except pagure.exceptions.PagureException as err:
Matt Prahl 489ad1
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 9c2953
        )
Matt Prahl 489ad1
Matt Prahl 489ad1
    jsonout = flask.jsonify(output)
Matt Prahl 489ad1
    return jsonout
mprahl 7d811e
mprahl 7d811e
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/git/branch", methods=["POST"])</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/git/branch", methods=["POST"])</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/git/branch", methods=["POST"])</repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>/git/branch", methods=["POST"])</repo></namespace></username>
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["create_branch"])
mprahl 7d811e
@api_method
mprahl 7d811e
def api_new_branch(repo, username=None, namespace=None):
mprahl 7d811e
    """
mprahl 7d811e
    Create a new git branch on a project
mprahl 7d811e
    ------------------------------------
mprahl 7d811e
    Create a new git branch on a project
mprahl 7d811e
mprahl 7d811e
    ::
mprahl 7d811e
mprahl 7d811e
        POST /api/0/rpms/python-requests/git/branch
mprahl 7d811e
mprahl 7d811e
mprahl 7d811e
    Input
mprahl 7d811e
    ^^^^^
mprahl 7d811e
mprahl 7d811e
    +------------------+---------+--------------+---------------------------+
mprahl 7d811e
    | Key              | Type    | Optionality  | Description               |
mprahl 7d811e
    +==================+=========+==============+===========================+
mprahl 7d811e
    | ``branch``       | string  | Mandatory    | | A string of the branch  |
mprahl 7d811e
    |                  |         |              |   to create.              |
mprahl 7d811e
    +------------------+---------+--------------+---------------------------+
mprahl 7d811e
    | ``from_branch``  | string  | Optional     | | A string of the branch  |
mprahl 7d811e
    |                  |         |              |   to branch off of. This  |
mprahl 7d811e
    |                  |         |              |   defaults to "master".   |
mprahl 97e56a
    |                  |         |              |   if ``from_commit``      |
mprahl 97e56a
    |                  |         |              |   isn't set.              |
mprahl 97e56a
    +------------------+---------+--------------+---------------------------+
mprahl 97e56a
    | ``from_commit``  | string  | Optional     | | A string of the commit  |
mprahl 97e56a
    |                  |         |              |   to branch off of.       |
mprahl 7d811e
    +------------------+---------+--------------+---------------------------+
mprahl 7d811e
mprahl 7d811e
mprahl 7d811e
    Sample response
mprahl 7d811e
    ^^^^^^^^^^^^^^^
mprahl 7d811e
mprahl 7d811e
    ::
mprahl 7d811e
mprahl 7d811e
        {
mprahl 7d811e
          'message': 'Project branch was created'
mprahl 7d811e
        }
mprahl 7d811e
mprahl 7d811e
    """
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Pierre-Yves Chibon 446bcc
mprahl 7d811e
    # Check if it's JSON or form data
Pierre-Yves Chibon 9c2953
    if flask.request.headers.get("Content-Type") == "application/json":
mprahl 7d811e
        # Set force to True to ignore the mimetype. Set silent so that None is
mprahl 7d811e
        # returned if it's invalid JSON.
mprahl 7d811e
        args = flask.request.get_json(force=True, silent=True) or {}
mprahl 7d811e
    else:
Slavek Kabrda 727932
        args = get_request_data()
mprahl 7d811e
Pierre-Yves Chibon 9c2953
    branch = args.get("branch")
Pierre-Yves Chibon 9c2953
    from_branch = args.get("from_branch")
Pierre-Yves Chibon 9c2953
    from_commit = args.get("from_commit")
mprahl 7d811e
mprahl 7d811e
    if from_branch and from_commit:
mprahl 7d811e
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
mprahl 7d811e
Pierre-Yves Chibon 9c2953
    if (
Pierre-Yves Chibon 9c2953
        not branch
Pierre-Yves Chibon 9c2953
        or not isinstance(branch, string_types)
Pierre-Yves Chibon 9c2953
        or (from_branch and not isinstance(from_branch, string_types))
Pierre-Yves Chibon 9c2953
        or (from_commit and not isinstance(from_commit, string_types))
Pierre-Yves Chibon 9c2953
    ):
mprahl 7d811e
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
mprahl 7d811e
mprahl 7d811e
    try:
Pierre-Yves Chibon 9c2953
        pagure.lib.git.new_git_branch(
Patrick Uiterwijk 03a519
            flask.g.fas_user.username,
Patrick Uiterwijk 03a519
            project,
Patrick Uiterwijk 03a519
            branch,
Patrick Uiterwijk 03a519
            from_branch=from_branch,
Patrick Uiterwijk 03a519
            from_commit=from_commit,
Pierre-Yves Chibon 9c2953
        )
mprahl 7d811e
    except GitError:  # pragma: no cover
mprahl 7d811e
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EGITERROR)
mprahl 7d811e
    except pagure.exceptions.PagureException as error:
mprahl 7d811e
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.ENOCODE, error=str(error)
Pierre-Yves Chibon 9c2953
        )
mprahl 7d811e
Pierre-Yves Chibon 9c2953
    output = {"message": "Project branch was created"}
mprahl 7d811e
    jsonout = flask.jsonify(output)
mprahl 7d811e
    return jsonout
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon faedb1
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/c/<commit_hash>/flag")</commit_hash></repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/c/<commit_hash>/flag")</commit_hash></repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/c/<commit_hash>/flag")</commit_hash></repo></username>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<namespace>/<repo>/c/<commit_hash>/flag")</commit_hash></repo></namespace></username>
Slavek Kabrda 3e4583
@api_method
Slavek Kabrda 3e4583
def api_commit_flags(repo, commit_hash, username=None, namespace=None):
Slavek Kabrda 3e4583
    """
Slavek Kabrda 3e4583
    Flags for a commit
Slavek Kabrda 3e4583
    ------------------
Slavek Kabrda 3e4583
    Return all flags for given commit of given project
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
    ::
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
        GET /api/0/<repo>/c/<commit_hash>/flag</commit_hash></repo>
Slavek Kabrda 3e4583
        GET /api/0/<namespace>/<repo>/c/<commit_hash>/flag</commit_hash></repo></namespace>
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
    ::
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
        GET /api/0/fork/<username>/<repo>/c/<commit_hash>/flag</commit_hash></repo></username>
Slavek Kabrda 3e4583
        GET /api/0/fork/<username>/<namespace>/<repo>/c/<commit_hash>/flag</commit_hash></repo></namespace></username>
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
    Sample response
Slavek Kabrda 3e4583
    ^^^^^^^^^^^^^^^
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
    ::
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
        {
Slavek Kabrda 3e4583
          "flags": [
Slavek Kabrda 3e4583
            {
Slavek Kabrda 3e4583
              "comment": "flag-comment",
Slavek Kabrda 3e4583
              "commit_hash": "28f1f7fe844301f0e5f7aecacae0a1e5ec50a090",
Slavek Kabrda 3e4583
              "date_created": "1520341983",
Slavek Kabrda 3e4583
              "percent": null,
Slavek Kabrda 3e4583
              "status": "success",
Slavek Kabrda 3e4583
              "url": "https://some.url.com",
Slavek Kabrda 3e4583
              "user": {
Slavek Kabrda 3e4583
                "fullname": "Full name",
Slavek Kabrda 3e4583
                "name": "fname"
Slavek Kabrda 3e4583
              },
Slavek Kabrda 3e4583
              "username": "somename"
Slavek Kabrda 3e4583
            },
Slavek Kabrda 3e4583
            {
Slavek Kabrda 3e4583
              "comment": "different-comment",
Slavek Kabrda 3e4583
              "commit_hash": "28f1f7fe844301f0e5f7aecacae0a1e5ec50a090",
Slavek Kabrda 3e4583
              "date_created": "1520512543",
Slavek Kabrda 3e4583
              "percent": null,
Slavek Kabrda 3e4583
              "status": "pending",
Slavek Kabrda 3e4583
              "url": "https://other.url.com",
Slavek Kabrda 3e4583
              "user": {
Slavek Kabrda 3e4583
                "fullname": "Other Name",
Slavek Kabrda 3e4583
                "name": "oname"
Slavek Kabrda 3e4583
              },
Slavek Kabrda 3e4583
              "username": "differentname"
Slavek Kabrda 3e4583
            }
Slavek Kabrda 3e4583
          ],
Slavek Kabrda 3e4583
          "total_flags": 2
Slavek Kabrda 3e4583
        }
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
    """
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Slavek Kabrda 3e4583
Slavek Kabrda 3e4583
    reponame = pagure.utils.get_repo_path(repo)
Slavek Kabrda 3e4583
    repo_obj = Repository(reponame)
Slavek Kabrda 3e4583
    try:
Slavek Kabrda 3e4583
        repo_obj.get(commit_hash)
Slavek Kabrda 3e4583
    except ValueError:
Pierre-Yves Chibon 9c2953
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOCOMMIT)
Slavek Kabrda 3e4583
Pierre-Yves Chibon 930073
    flags = pagure.lib.query.get_commit_flag(
Pierre-Yves Chibon 930073
        flask.g.session, repo, commit_hash
Pierre-Yves Chibon 930073
    )
Slavek Kabrda 3e4583
    flags = [f.to_json(public=True) for f in flags]
Pierre-Yves Chibon 9c2953
    return flask.jsonify({"total_flags": len(flags), "flags": flags})
Slavek Kabrda 3e4583
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/c/<commit_hash>/flag", methods=["POST"])</commit_hash></repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/c/<commit_hash>/flag", methods=["POST"])</commit_hash></repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/c/<commit_hash>/flag", methods=["POST"])</commit_hash></repo></username>
Pierre-Yves Chibon 8715b5
@API.route(
Pierre-Yves Chibon 9c2953
    "/fork/<username>/<namespace>/<repo>/c/<commit_hash>/flag",</commit_hash></repo></namespace></username>
Pierre-Yves Chibon 9c2953
    methods=["POST"],
Pierre-Yves Chibon 9c2953
)
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["commit_flag"])
Pierre-Yves Chibon 8715b5
@api_method
Pierre-Yves Chibon 8715b5
def api_commit_add_flag(repo, commit_hash, username=None, namespace=None):
Pierre-Yves Chibon 8715b5
    """
Pierre-Yves Chibon 8715b5
    Flag a commit
Pierre-Yves Chibon 8715b5
    -------------------
Pierre-Yves Chibon 8715b5
    Add or edit flags on a commit.
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    ::
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
        POST /api/0/<repo>/c/<commit_hash>/flag</commit_hash></repo>
Pierre-Yves Chibon 8715b5
        POST /api/0/<namespace>/<repo>/c/<commit_hash>/flag</commit_hash></repo></namespace>
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    ::
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
        POST /api/0/fork/<username>/<repo>/c/<commit_hash>/flag</commit_hash></repo></username>
Pierre-Yves Chibon 8715b5
        POST /api/0/fork/<username>/<namespace>/<repo>/c/<commit_hash>/flag</commit_hash></repo></namespace></username>
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    Input
Pierre-Yves Chibon 8715b5
    ^^^^^
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon 8715b5
    | Key           | Type    | Optionality  | Description                 |
Pierre-Yves Chibon 8715b5
    +===============+=========+==============+=============================+
Pierre-Yves Chibon 8715b5
    | ``username``  | string  | Mandatory    | | The name of the           |
Pierre-Yves Chibon 8715b5
    |               |         |              |   application to be         |
Pierre-Yves Chibon 8715b5
    |               |         |              |   presented to users        |
Pierre-Yves Chibon abcbf7
    |               |         |              |   on the commit pages       |
Pierre-Yves Chibon 8715b5
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon 8715b5
    | ``comment``   | string  | Mandatory    | | A short message           |
Pierre-Yves Chibon 8715b5
    |               |         |              |   summarizing the           |
Pierre-Yves Chibon 8715b5
    |               |         |              |   presented results         |
Pierre-Yves Chibon 8715b5
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon 8715b5
    | ``url``       | string  | Mandatory    | | A URL to the result       |
Pierre-Yves Chibon 8715b5
    |               |         |              |   of this flag              |
Pierre-Yves Chibon 8715b5
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon abcbf7
    | ``status``    | string  | Mandatory    | | The status of the task,   |
Slavek Kabrda 45252f
    |               |         |              |   can be any of:            |
Slavek Kabrda 45252f
    |               |         |              |   $$FLAG_STATUSES_COMMAS$$  |
Pierre-Yves Chibon abcbf7
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon abcbf7
    | ``percent``   | int     | Optional     | | A percentage of           |
Pierre-Yves Chibon abcbf7
    |               |         |              |   completion compared to    |
Pierre-Yves Chibon abcbf7
    |               |         |              |   the goal. The percentage  |
Pierre-Yves Chibon abcbf7
    |               |         |              |   also determine the        |
Pierre-Yves Chibon abcbf7
    |               |         |              |   background color of the   |
Pierre-Yves Chibon abcbf7
    |               |         |              |   flag on the pages         |
Pierre-Yves Chibon abcbf7
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon 8715b5
    | ``uid``       | string  | Optional     | | A unique identifier used  |
Pierre-Yves Chibon abcbf7
    |               |         |              |   to identify a flag across |
Pierre-Yves Chibon abcbf7
    |               |         |              |   all projects. If the      |
Pierre-Yves Chibon 8715b5
    |               |         |              |   provided UID matches an   |
Pierre-Yves Chibon 8715b5
    |               |         |              |   existing one, then the    |
Pierre-Yves Chibon 8715b5
    |               |         |              |   API call will update the  |
Pierre-Yves Chibon 8715b5
    |               |         |              |   existing one rather than  |
Pierre-Yves Chibon 8715b5
    |               |         |              |   create a new one.         |
Pierre-Yves Chibon 8715b5
    |               |         |              |   Maximum Length: 32        |
Pierre-Yves Chibon 8715b5
    |               |         |              |   characters. Default: an   |
Pierre-Yves Chibon 8715b5
    |               |         |              |   auto generated UID        |
Pierre-Yves Chibon 8715b5
    +---------------+---------+--------------+-----------------------------+
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    Sample response
Pierre-Yves Chibon 8715b5
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    ::
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
        {
Pierre-Yves Chibon 8715b5
          "flag": {
Pierre-Yves Chibon 8715b5
              "comment": "Tests passed",
Pierre-Yves Chibon 8715b5
              "commit_hash": "62b49f00d489452994de5010565fab81",
Pierre-Yves Chibon 8715b5
              "date_created": "1510742565",
Pierre-Yves Chibon 8715b5
              "percent": 100,
Pierre-Yves Chibon abcbf7
              "status": "success",
Pierre-Yves Chibon 8715b5
              "url": "http://jenkins.cloud.fedoraproject.org/",
Pierre-Yves Chibon 8715b5
              "user": {
Pierre-Yves Chibon 8715b5
                "default_email": "bar@pingou.com",
Pierre-Yves Chibon 8715b5
                "emails": ["bar@pingou.com", "foo@pingou.com"],
Pierre-Yves Chibon 8715b5
                "fullname": "PY C",
Pierre-Yves Chibon 8715b5
                "name": "pingou"},
Pierre-Yves Chibon 8715b5
              "username": "Jenkins"
Pierre-Yves Chibon 8715b5
            },
Pierre-Yves Chibon 8715b5
            "message": "Flag added",
Pierre-Yves Chibon 8715b5
            "uid": "b1de8f80defd4a81afe2e09f39678087"
Pierre-Yves Chibon 8715b5
        }
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    ::
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
        {
Pierre-Yves Chibon 8715b5
          "flag": {
Pierre-Yves Chibon 8715b5
              "comment": "Tests passed",
Pierre-Yves Chibon 8715b5
              "commit_hash": "62b49f00d489452994de5010565fab81",
Pierre-Yves Chibon 8715b5
              "date_created": "1510742565",
Pierre-Yves Chibon 8715b5
              "percent": 100,
Pierre-Yves Chibon abcbf7
              "status": "success",
Pierre-Yves Chibon 8715b5
              "url": "http://jenkins.cloud.fedoraproject.org/",
Pierre-Yves Chibon 8715b5
              "user": {
Pierre-Yves Chibon 8715b5
                "default_email": "bar@pingou.com",
Pierre-Yves Chibon 8715b5
                "emails": ["bar@pingou.com", "foo@pingou.com"],
Pierre-Yves Chibon 8715b5
                "fullname": "PY C",
Pierre-Yves Chibon 8715b5
                "name": "pingou"},
Pierre-Yves Chibon 8715b5
              "username": "Jenkins"
Pierre-Yves Chibon 8715b5
            },
Pierre-Yves Chibon 8715b5
            "message": "Flag updated",
Pierre-Yves Chibon 8715b5
            "uid": "b1de8f80defd4a81afe2e09f39678087"
Pierre-Yves Chibon 8715b5
        }
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    """  # noqa
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 01d0f2
    repo = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(repo, project_token=False)
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    output = {}
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon b130e5
    reponame = pagure.utils.get_repo_path(repo)
Pierre-Yves Chibon 8715b5
    repo_obj = Repository(reponame)
Pierre-Yves Chibon 8715b5
    try:
Pierre-Yves Chibon 8715b5
        repo_obj.get(commit_hash)
Pierre-Yves Chibon 8715b5
    except ValueError:
Pierre-Yves Chibon 9c2953
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOCOMMIT)
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 5c561c
    form = pagure.forms.AddPullRequestFlagForm(csrf_enabled=False)
Pierre-Yves Chibon 8715b5
    if form.validate_on_submit():
Pierre-Yves Chibon 8715b5
        username = form.username.data
Pierre-Yves Chibon abcbf7
        percent = form.percent.data.strip() or None
Pierre-Yves Chibon 8715b5
        comment = form.comment.data.strip()
Pierre-Yves Chibon 8715b5
        url = form.url.data.strip()
Pierre-Yves Chibon 8715b5
        uid = form.uid.data.strip() if form.uid.data else None
Pierre-Yves Chibon abcbf7
        status = form.status.data.strip()
Pierre-Yves Chibon 8715b5
        try:
Pierre-Yves Chibon 8715b5
            # New Flag
Pierre-Yves Chibon 930073
            message, uid = pagure.lib.query.add_commit_flag(
Pierre-Yves Chibon b130e5
                session=flask.g.session,
Pierre-Yves Chibon 8715b5
                repo=repo,
Pierre-Yves Chibon 8715b5
                commit_hash=commit_hash,
Pierre-Yves Chibon 8715b5
                username=username,
Pierre-Yves Chibon 8715b5
                percent=percent,
Pierre-Yves Chibon 8715b5
                comment=comment,
Pierre-Yves Chibon abcbf7
                status=status,
Pierre-Yves Chibon 8715b5
                url=url,
Pierre-Yves Chibon 8715b5
                uid=uid,
Pierre-Yves Chibon 8715b5
                user=flask.g.fas_user.username,
Pierre-Yves Chibon 8715b5
                token=flask.g.token.id,
Pierre-Yves Chibon 8715b5
            )
Pierre-Yves Chibon b130e5
            flask.g.session.commit()
Pierre-Yves Chibon 930073
            c_flag = pagure.lib.query.get_commit_flag_by_uid(
Pierre-Yves Chibon 9c2953
                flask.g.session, commit_hash, uid
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 9c2953
            output["message"] = message
Pierre-Yves Chibon 9c2953
            output["uid"] = uid
Pierre-Yves Chibon 9c2953
            output["flag"] = c_flag.to_json()
Pierre-Yves Chibon 8715b5
        except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon 8715b5
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 8715b5
        except SQLAlchemyError as err:  # pragma: no cover
Pierre-Yves Chibon b130e5
            flask.g.session.rollback()
Pierre-Yves Chibon 5460c9
            _log.exception(err)
Pierre-Yves Chibon 9c2953
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Pierre-Yves Chibon 8715b5
    else:
Pierre-Yves Chibon 8715b5
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    jsonout = flask.jsonify(output)
Pierre-Yves Chibon 8715b5
    return jsonout
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/watchers/update", methods=["POST"])</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/watchers/update", methods=["POST"])</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/watchers/update", methods=["POST"])</repo></username>
Pierre-Yves Chibon f778bd
@API.route(
Pierre-Yves Chibon 9c2953
    "/fork/<username>/<namespace>/<repo>/watchers/update", methods=["POST"]</repo></namespace></username>
Pierre-Yves Chibon 9c2953
)
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["update_watch_status"])
Pierre-Yves Chibon f778bd
@api_method
Pierre-Yves Chibon f778bd
def api_update_project_watchers(repo, username=None, namespace=None):
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon f778bd
    Update project watchers
Pierre-Yves Chibon f778bd
    -----------------------
Pierre-Yves Chibon f778bd
    Allows anyone to update their own watch status on the project.
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    ::
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
        POST /api/0/<repo>/watchers/update</repo>
Pierre-Yves Chibon f778bd
        POST /api/0/<namespace>/<repo>/watchers/update</repo></namespace>
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    ::
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
        POST /api/0/fork/<username>/<repo>/watchers/update</repo></username>
Pierre-Yves Chibon f778bd
        POST /api/0/fork/<username>/<namespace>/<repo>/watchers/update</repo></namespace></username>
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    Input
Pierre-Yves Chibon f778bd
    ^^^^^
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon f778bd
    | Key              | Type    | Optionality  | Description               |
Pierre-Yves Chibon f778bd
    +==================+=========+==============+===========================+
Pierre-Yves Chibon f778bd
    | ``repo``         | string  | Mandatory    | | The name of the project |
Pierre-Yves Chibon f778bd
    |                  |         |              |   to fork.                |
Pierre-Yves Chibon f778bd
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon f778bd
    | ``status``       | string  | Mandatory    | | The new watch status to |
Pierre-Yves Chibon f778bd
    |                  |         |              |   set on that project.    |
Pierre-Yves Chibon f778bd
    |                  |         |              |   (See options below)     |
Pierre-Yves Chibon f778bd
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon f778bd
    | ``watcher``      | string  | Mandatory    | | The name of the user    |
Pierre-Yves Chibon f778bd
    |                  |         |              |   changing their watch    |
Pierre-Yves Chibon f778bd
    |                  |         |              |   status.                 |
Pierre-Yves Chibon f778bd
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon f778bd
    | ``namespace``    | string  | Optional     | | The namespace of the    |
Pierre-Yves Chibon f778bd
    |                  |         |              |   project to fork.        |
Pierre-Yves Chibon f778bd
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon f778bd
    | ``username``     | string  | Optional     | | The username of the user|
Pierre-Yves Chibon f778bd
    |                  |         |              |   of the fork.            |
Pierre-Yves Chibon f778bd
    +------------------+---------+--------------+---------------------------+
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    Watch Status
Pierre-Yves Chibon f778bd
    ^^^^^^^^^^^^
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    +------------+----------------------------------------------+
Pierre-Yves Chibon f778bd
    | Key        | Description                                  |
Pierre-Yves Chibon f778bd
    +============+==============================================+
Pierre-Yves Chibon f778bd
    | -1         | Reset the watch status to default            |
Pierre-Yves Chibon f778bd
    +------------+----------------------------------------------+
Pierre-Yves Chibon f778bd
    | 0          | Unwatch, don't notify the user of anything   |
Pierre-Yves Chibon f778bd
    +------------+----------------------------------------------+
Pierre-Yves Chibon f778bd
    | 1          | Watch issues and pull-requests               |
Pierre-Yves Chibon f778bd
    +------------+----------------------------------------------+
Pierre-Yves Chibon f778bd
    | 2          | Watch commits                                |
Pierre-Yves Chibon f778bd
    +------------+----------------------------------------------+
Pierre-Yves Chibon f778bd
    | 3          | Watch commits, issues and pull-requests      |
Pierre-Yves Chibon f778bd
    +------------+----------------------------------------------+
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    Sample response
Pierre-Yves Chibon f778bd
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    ::
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
        {
Pierre-Yves Chibon f778bd
            "message": "You are now watching issues and PRs on this project",
Pierre-Yves Chibon f778bd
            "status": "ok"
Pierre-Yves Chibon f778bd
        }
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project)
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    # Get the input submitted
Pierre-Yves Chibon f778bd
    data = get_request_data()
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon 9c2953
    watcher = data.get("watcher")
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    if not watcher:
Pierre-Yves Chibon 9c2953
        _log.debug("api_update_project_watchers: Invalid watcher: %s", watcher)
Pierre-Yves Chibon 9c2953
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    is_site_admin = pagure.utils.is_admin()
Pierre-Yves Chibon f778bd
    # Only allow the main admin, and the user themselves to update their
Pierre-Yves Chibon f778bd
    # status
Pierre-Yves Chibon f778bd
    if not is_site_admin and flask.g.fas_user.username != watcher:
Pierre-Yves Chibon f778bd
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            401, error_code=APIERROR.EMODIFYPROJECTNOTALLOWED
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    try:
Pierre-Yves Chibon 930073
        pagure.lib.query.get_user(flask.g.session, watcher)
Karsten Hopp 3b333e
    except pagure.exceptions.PagureException:
Pierre-Yves Chibon f778bd
        _log.debug(
Pierre-Yves Chibon 9c2953
            "api_update_project_watchers: Invalid user watching: %s", watcher
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9c2953
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon 9c2953
    watch_status = data.get("status")
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon f778bd
    try:
Pierre-Yves Chibon 930073
        msg = pagure.lib.query.update_watch_status(
Pierre-Yves Chibon f778bd
            session=flask.g.session,
Pierre-Yves Chibon f778bd
            project=project,
Pierre-Yves Chibon f778bd
            user=watcher,
Pierre-Yves Chibon 9c2953
            watch=watch_status,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon f778bd
        flask.g.session.commit()
Pierre-Yves Chibon f778bd
    except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon f778bd
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon f778bd
    except SQLAlchemyError as err:  # pragma: no cover
Pierre-Yves Chibon f778bd
        flask.g.session.rollback()
Pierre-Yves Chibon f778bd
        _log.exception(err)
Pierre-Yves Chibon 9c2953
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Pierre-Yves Chibon f778bd
Pierre-Yves Chibon 9c2953
    return flask.jsonify({"message": msg, "status": "ok"})
Karsten Hopp be01e8
Karsten Hopp be01e8
Pierre-Yves Chibon 9c2953
@API.route("/<repo>/git/modifyacls", methods=["POST"])</repo>
Pierre-Yves Chibon 9c2953
@API.route("/<namespace>/<repo>/git/modifyacls", methods=["POST"])</repo></namespace>
Pierre-Yves Chibon 9c2953
@API.route("/fork/<username>/<repo>/git/modifyacls", methods=["POST"])</repo></username>
Pierre-Yves Chibon 9c2953
@API.route(
Pierre-Yves Chibon 9c2953
    "/fork/<username>/<namespace>/<repo>/git/modifyacls", methods=["POST"]</repo></namespace></username>
Pierre-Yves Chibon 9c2953
)
Pierre-Yves Chibon 9c2953
@api_login_required(acls=["modify_project"])
Karsten Hopp be01e8
@api_method
Karsten Hopp be01e8
def api_modify_acls(repo, namespace=None, username=None):
Karsten Hopp be01e8
    """
Karsten Hopp be01e8
    Modify ACLs on a project
Karsten Hopp be01e8
    ------------------------
Karsten Hopp be01e8
    Add, remove or update ACLs on a project for a particular user or group.
Karsten Hopp be01e8
Karsten Hopp be01e8
    This is restricted to project admins.
Karsten Hopp be01e8
Karsten Hopp be01e8
    ::
Karsten Hopp be01e8
Igor Gnatenko 39113c
        POST /api/0/<repo>/git/modifyacls</repo>
Igor Gnatenko 39113c
        POST /api/0/<namespace>/<repo>/git/modifyacls</repo></namespace>
Karsten Hopp be01e8
Karsten Hopp be01e8
    ::
Karsten Hopp be01e8
Igor Gnatenko 39113c
        POST /api/0/fork/<username>/<repo>/git/modifyacls</repo></username>
Igor Gnatenko 39113c
        POST /api/0/fork/<username>/<namespace>/<repo>/git/modifyacls</repo></namespace></username>
Karsten Hopp be01e8
Karsten Hopp be01e8
Karsten Hopp be01e8
    Input
Karsten Hopp be01e8
    ^^^^^
Karsten Hopp be01e8
Karsten Hopp be01e8
    +------------------+---------+---------------+---------------------------+
Karsten Hopp be01e8
    | Key              | Type    | Optionality   | Description               |
Karsten Hopp be01e8
    +==================+=========+===============+===========================+
Karsten Hopp be01e8
    | ``user_type``    | String  | Mandatory     | A string to specify if    |
Karsten Hopp be01e8
    |                  |         |               | the ACL should be changed |
Karsten Hopp be01e8
    |                  |         |               | for a user or a group.    |
Karsten Hopp be01e8
    |                  |         |               | Specifying one of either  |
Karsten Hopp be01e8
    |                  |         |               | 'user' or 'group' is      |
Karsten Hopp be01e8
    |                  |         |               | mandatory                 |
Karsten Hopp be01e8
    |                  |         |               |                           |
Karsten Hopp be01e8
    +------------------+---------+---------------+---------------------------+
Karsten Hopp be01e8
    | ``name``         | String  | Mandatory     | The name of the user or   |
Karsten Hopp be01e8
    |                  |         |               | group whose ACL           |
Karsten Hopp be01e8
    |                  |         |               | should be changed.        |
Karsten Hopp be01e8
    |                  |         |               |                           |
Karsten Hopp be01e8
    +------------------+---------+---------------+---------------------------+
Pierre-Yves Chibon 6ffde7
    | ``acl``          | String  | Optional      | Can be either unspecified,|
Karsten Hopp be01e8
    |                  |         |               | 'ticket', 'commit',       |
Pierre-Yves Chibon 6ffde7
    |                  |         |               | 'admin'. If unspecified,  |
Pierre-Yves Chibon 6ffde7
    |                  |         |               | the access will be removed|
Karsten Hopp be01e8
    |                  |         |               |                           |
Karsten Hopp be01e8
    +------------------+---------+---------------+---------------------------+
Karsten Hopp be01e8
Karsten Hopp be01e8
Karsten Hopp be01e8
    Sample response
Karsten Hopp be01e8
    ^^^^^^^^^^^^^^^
Karsten Hopp be01e8
Karsten Hopp be01e8
    ::
Karsten Hopp be01e8
Karsten Hopp be01e8
        {
Karsten Hopp be01e8
          "access_groups": {
Karsten Hopp be01e8
            "admin": [],
Karsten Hopp be01e8
            "commit": [],
Karsten Hopp be01e8
            "ticket": []
Karsten Hopp be01e8
          },
Karsten Hopp be01e8
          "access_users": {
Karsten Hopp be01e8
            "admin": [],
Karsten Hopp be01e8
            "commit": [
Karsten Hopp be01e8
              "ta2"
Karsten Hopp be01e8
            ],
Karsten Hopp be01e8
            "owner": [
Karsten Hopp be01e8
              "karsten"
Karsten Hopp be01e8
            ],
Karsten Hopp be01e8
            "ticket": [
Karsten Hopp be01e8
              "ta1"
Karsten Hopp be01e8
            ]
Karsten Hopp be01e8
          },
Karsten Hopp be01e8
          "close_status": [],
Karsten Hopp be01e8
          "custom_keys": [],
Karsten Hopp be01e8
          "date_created": "1531131619",
Karsten Hopp be01e8
          "date_modified": "1531302337",
Karsten Hopp be01e8
          "description": "pagure local instance",
Karsten Hopp be01e8
          "fullname": "pagure",
Karsten Hopp be01e8
          "id": 1,
Karsten Hopp be01e8
          "milestones": {},
Karsten Hopp be01e8
          "name": "pagure",
Karsten Hopp be01e8
          "namespace": null,
Karsten Hopp be01e8
          "parent": null,
Karsten Hopp be01e8
          "priorities": {},
Karsten Hopp be01e8
          "tags": [],
Karsten Hopp be01e8
          "url_path": "pagure",
Karsten Hopp be01e8
          "user": {
Karsten Hopp be01e8
            "fullname": "KH",
Karsten Hopp be01e8
            "name": "karsten"
Karsten Hopp be01e8
          }
Karsten Hopp be01e8
        }
Karsten Hopp be01e8
Karsten Hopp be01e8
    """
Karsten Hopp be01e8
    output = {}
Karsten Hopp be01e8
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Karsten Hopp be01e8
Karsten Hopp be01e8
    form = pagure.forms.ModifyACLForm(csrf_enabled=False)
Karsten Hopp be01e8
    if form.validate_on_submit():
Pierre-Yves Chibon 6ffde7
        acl = form.acl.data
Pierre-Yves Chibon 6ffde7
        group = None
Pierre-Yves Chibon 6ffde7
        user = None
Pierre-Yves Chibon 9c2953
        if form.user_type.data == "user":
Karsten Hopp be01e8
            user = form.name.data
Karsten Hopp be01e8
        else:
Karsten Hopp be01e8
            group = form.name.data
Pierre-Yves Chibon 6ffde7
Pierre-Yves Chibon 6ffde7
        is_site_admin = pagure.utils.is_admin()
Pierre-Yves Chibon 9c2953
        admins = [u.username for u in project.get_project_users("admin")]
Pierre-Yves Chibon 6ffde7
Pierre-Yves Chibon 6ffde7
        if not acl:
Pierre-Yves Chibon 9c2953
            if (
Pierre-Yves Chibon 9c2953
                user
Pierre-Yves Chibon 9c2953
                and flask.g.fas_user.username != user
Pierre-Yves Chibon 9c2953
                and flask.g.fas_user.username not in admins
Pierre-Yves Chibon 9c2953
                and flask.g.fas_user.username != project.user.username
Pierre-Yves Chibon 9c2953
                and not is_site_admin
Pierre-Yves Chibon 9c2953
            ):
Pierre-Yves Chibon 6ffde7
                raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                    401, error_code=APIERROR.EMODIFYPROJECTNOTALLOWED
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 9c2953
        elif (
Pierre-Yves Chibon 9c2953
            flask.g.fas_user.username not in admins
Pierre-Yves Chibon 9c2953
            and flask.g.fas_user.username != project.user.username
Pierre-Yves Chibon 9c2953
            and not is_site_admin
Pierre-Yves Chibon 9c2953
        ):
Pierre-Yves Chibon 6ffde7
            raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                401, error_code=APIERROR.EMODIFYPROJECTNOTALLOWED
Pierre-Yves Chibon 9c2953
            )
Karsten Hopp be01e8
Karsten Hopp be01e8
        if user:
Pierre-Yves Chibon 930073
            user_obj = pagure.lib.query.search_user(
Pierre-Yves Chibon 930073
                flask.g.session, username=user
Pierre-Yves Chibon 930073
            )
Karsten Hopp be01e8
            if not user_obj:
Karsten Hopp be01e8
                raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                    404, error_code=APIERROR.ENOUSER
Pierre-Yves Chibon 9c2953
                )
Karsten Hopp be01e8
Karsten Hopp be01e8
        elif group:
Pierre-Yves Chibon 930073
            group_obj = pagure.lib.query.search_groups(
Pierre-Yves Chibon 9c2953
                flask.g.session, group_name=group
Pierre-Yves Chibon 9c2953
            )
Karsten Hopp be01e8
            if not group_obj:
Karsten Hopp be01e8
                raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                    404, error_code=APIERROR.ENOGROUP
Pierre-Yves Chibon 9c2953
                )
Karsten Hopp be01e8
Pierre-Yves Chibon 6ffde7
        if acl:
Pierre-Yves Chibon 9c2953
            if (
Pierre-Yves Chibon 9c2953
                user
Pierre-Yves Chibon 9c2953
                and user_obj not in project.access_users[acl]
Pierre-Yves Chibon 9c2953
                and user_obj.user != project.user.user
Pierre-Yves Chibon 9c2953
            ):
Pierre-Yves Chibon 6ffde7
                _log.info(
Pierre-Yves Chibon 9c2953
                    "Adding user %s to project: %s", user, project.fullname
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 930073
                pagure.lib.query.add_user_to_project(
Pierre-Yves Chibon 6ffde7
                    session=flask.g.session,
Pierre-Yves Chibon 6ffde7
                    project=project,
Pierre-Yves Chibon 6ffde7
                    new_user=user,
Pierre-Yves Chibon 6ffde7
                    user=flask.g.fas_user.username,
Pierre-Yves Chibon 9c2953
                    access=acl,
Pierre-Yves Chibon 6ffde7
                )
Pierre-Yves Chibon 6ffde7
            elif group and group_obj not in project.access_groups[acl]:
Pierre-Yves Chibon 6ffde7
                _log.info(
Pierre-Yves Chibon 9c2953
                    "Adding group %s to project: %s", group, project.fullname
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 930073
                pagure.lib.query.add_group_to_project(
Pierre-Yves Chibon 6ffde7
                    session=flask.g.session,
Pierre-Yves Chibon 6ffde7
                    project=project,
Pierre-Yves Chibon 6ffde7
                    new_group=group,
Pierre-Yves Chibon 6ffde7
                    user=flask.g.fas_user.username,
Pierre-Yves Chibon 6ffde7
                    access=acl,
Pierre-Yves Chibon 9c2953
                    create=pagure_config.get("ENABLE_GROUP_MNGT", False),
Pierre-Yves Chibon 6ffde7
                    is_admin=pagure.utils.is_admin(),
Pierre-Yves Chibon 6ffde7
                )
Pierre-Yves Chibon 6ffde7
        else:
Pierre-Yves Chibon 6ffde7
            if user:
Pierre-Yves Chibon 6ffde7
                _log.info(
Pierre-Yves Chibon 9c2953
                    "Looking at removing user %s from project %s",
Pierre-Yves Chibon 9c2953
                    user,
Pierre-Yves Chibon 9c2953
                    project.fullname,
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 6ffde7
                try:
Pierre-Yves Chibon 930073
                    pagure.lib.query.remove_user_of_project(
Pierre-Yves Chibon 9c2953
                        flask.g.session,
Pierre-Yves Chibon 9c2953
                        user_obj,
Pierre-Yves Chibon 9c2953
                        project,
Pierre-Yves Chibon 9c2953
                        flask.g.fas_user.username,
Pierre-Yves Chibon 9c2953
                    )
Pierre-Yves Chibon 6ffde7
                except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon 6ffde7
                    raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
                        400, error_code=APIERROR.EINVALIDREQ, errors="%s" % err
Pierre-Yves Chibon 9c2953
                    )
Pierre-Yves Chibon 6ffde7
            elif group:
Pierre-Yves Chibon 6ffde7
                pass
Pierre-Yves Chibon 6ffde7
Karsten Hopp be01e8
        try:
Karsten Hopp be01e8
            flask.g.session.commit()
Karsten Hopp be01e8
        except pagure.exceptions.PagureException as msg:
Karsten Hopp be01e8
            flask.g.session.rollback()
Karsten Hopp be01e8
            _log.debug(msg)
Pierre-Yves Chibon 9c2953
            flask.flash(str(msg), "error")
Karsten Hopp be01e8
        except SQLAlchemyError as err:
Karsten Hopp be01e8
            _log.exception(err)
Karsten Hopp be01e8
            flask.g.session.rollback()
Pierre-Yves Chibon 9c2953
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Karsten Hopp be01e8
Karsten Hopp be01e8
        pagure.lib.git.generate_gitolite_acls(project=project)
Karsten Hopp be01e8
        output = project.to_json(api=True, public=True)
Karsten Hopp be01e8
    else:
Karsten Hopp be01e8
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 9c2953
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
Pierre-Yves Chibon 9c2953
        )
Karsten Hopp be01e8
Karsten Hopp be01e8
    jsonout = flask.jsonify(output)
Karsten Hopp be01e8
    return jsonout
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
@API.route("/<repo>/options", methods=["GET"])</repo>
Pierre-Yves Chibon 0ede45
@API.route("/<namespace>/<repo>/options", methods=["GET"])</repo></namespace>
Pierre-Yves Chibon 0ede45
@API.route("/fork/<username>/<repo>/options", methods=["GET"])</repo></username>
Pierre-Yves Chibon 0ede45
@API.route("/fork/<username>/<namespace>/<repo>/options", methods=["GET"])</repo></namespace></username>
Pierre-Yves Chibon 0ede45
@api_login_required(acls=["modify_project"])
Pierre-Yves Chibon 0ede45
@api_method
Pierre-Yves Chibon 0ede45
def api_get_project_options(repo, username=None, namespace=None):
Pierre-Yves Chibon 0ede45
    """
Pierre-Yves Chibon 0ede45
    Get project options
Pierre-Yves Chibon 0ede45
    ----------------------
Pierre-Yves Chibon 0ede45
    Allow project admins to retrieve the current options of a project.
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
    ::
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
        GET /api/0/<repo>/options</repo>
Pierre-Yves Chibon 0ede45
        GET /api/0/<namespace>/<repo>/options</repo></namespace>
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
    ::
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
        GET /api/0/fork/<username>/<repo>/options</repo></username>
Pierre-Yves Chibon 0ede45
        GET /api/0/fork/<username>/<namespace>/<repo>/options</repo></namespace></username>
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
    Sample response
Pierre-Yves Chibon 0ede45
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
    ::
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
        {
Pierre-Yves Chibon 0ede45
          "settings": {
Pierre-Yves Chibon 0ede45
            "Enforce_signed-off_commits_in_pull-request": false,
Pierre-Yves Chibon 0ede45
            "Minimum_score_to_merge_pull-request": -1,
Pierre-Yves Chibon 0ede45
            "Only_assignee_can_merge_pull-request": false,
Pierre-Yves Chibon 0ede45
            "Web-hooks": null,
Pierre-Yves Chibon 0ede45
            "always_merge": false,
Pierre-Yves Chibon 0ede45
            "disable_non_fast-forward_merges": false,
Pierre-Yves Chibon 0ede45
            "fedmsg_notifications": true,
Pierre-Yves Chibon 0ede45
            "issue_tracker": true,
Pierre-Yves Chibon 0ede45
            "issue_tracker_read_only": false,
Pierre-Yves Chibon 0ede45
            "issues_default_to_private": false,
Pierre-Yves Chibon 0ede45
            "notify_on_commit_flag": false,
Pierre-Yves Chibon 0ede45
            "notify_on_pull-request_flag": false,
Pierre-Yves Chibon 0ede45
            "open_metadata_access_to_all": false,
Pierre-Yves Chibon 0ede45
            "project_documentation": false,
Pierre-Yves Chibon 0ede45
            "pull_request_access_only": false,
Pierre-Yves Chibon 0ede45
            "pull_requests": true,
Pierre-Yves Chibon 0ede45
            "stomp_notifications": true
Pierre-Yves Chibon 0ede45
          },
Pierre-Yves Chibon 0ede45
          "status": "ok"
Pierre-Yves Chibon 0ede45
        }
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
    """
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Pierre-Yves Chibon 0ede45
Pierre-Yves Chibon 0ede45
    return flask.jsonify({"settings": project.settings, "status": "ok"})
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
Fabien Boucher dfb30c
@API.route("/<repo>/connector", methods=["GET"])</repo>
Fabien Boucher dfb30c
@API.route("/<namespace>/<repo>/connector", methods=["GET"])</repo></namespace>
Fabien Boucher dfb30c
@API.route("/fork/<username>/<repo>/connector", methods=["GET"])</repo></username>
Fabien Boucher dfb30c
@API.route("/fork/<username>/<namespace>/<repo>/connector", methods=["GET"])</repo></namespace></username>
Fabien Boucher dfb30c
@api_login_required(acls=["modify_project"])
Fabien Boucher dfb30c
@api_method
Fabien Boucher dfb30c
def api_get_project_connector(repo, username=None, namespace=None):
Fabien Boucher dfb30c
    """
Fabien Boucher dfb30c
    Get project connector
Fabien Boucher dfb30c
    ---------------------
Fabien Boucher 0bccc8
    Allow project owners and admins to retrieve their own connector tokens.
Fabien Boucher dfb30c
    Connector tokens are the API tokens and the Web Hook token
Fabien Boucher dfb30c
    of the project. Connector tokens make possible for an external
Fabien Boucher dfb30c
    application to listen and verify project notifications and act
Fabien Boucher dfb30c
    on project via the REST API.
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    ::
Fabien Boucher dfb30c
Fabien Boucher dfb30c
        GET /api/0/<repo>/connector</repo>
Fabien Boucher dfb30c
        GET /api/0/<namespace>/<repo>/connector</repo></namespace>
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    ::
Fabien Boucher dfb30c
Fabien Boucher dfb30c
        GET /api/0/fork/<username>/<repo>/connector</repo></username>
Fabien Boucher dfb30c
        GET /api/0/fork/<username>/<namespace>/<repo>/connector</repo></namespace></username>
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    Sample response
Fabien Boucher dfb30c
    ^^^^^^^^^^^^^^^
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    ::
Fabien Boucher dfb30c
Fabien Boucher dfb30c
        {
Fabien Boucher dfb30c
          "connector": {
Fabien Boucher dfb30c
              "hook_token": "aaabbbccc",
Fabien Boucher dfb30c
              "api_token": [
Fabien Boucher 0bccc8
                  {'name': 'foo token',
Fabien Boucher 0bccc8
                   'id': "abcdefoo",
Fabien Boucher 0bccc8
                   'expired': True}
Fabien Boucher 0bccc8
                  {'name': 'bar token',
Fabien Boucher 0bccc8
                   'id': "abcdebar",
Fabien Boucher 0bccc8
                   'expired': False}
Fabien Boucher dfb30c
              ]
Fabien Boucher dfb30c
          },
Fabien Boucher dfb30c
          "status": "ok"
Fabien Boucher dfb30c
        }
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    """
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    authorized_users = [project.user.username]
Fabien Boucher dfb30c
    authorized_users.extend(
Karsten Hopp 6ce2d7
        [user.user for user in project.access_users["admin"]]
Karsten Hopp 6ce2d7
    )
Fabien Boucher dfb30c
    if flask.g.fas_user.user not in authorized_users:
Fabien Boucher dfb30c
        raise pagure.exceptions.APIError(
Karsten Hopp 6ce2d7
            401, error_code=APIERROR.ENOTHIGHENOUGH
Karsten Hopp 6ce2d7
        )
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    user_obj = pagure.lib.query.search_user(
Karsten Hopp 6ce2d7
        flask.g.session, username=flask.g.fas_user.user
Karsten Hopp 6ce2d7
    )
Fabien Boucher dfb30c
    user_project_tokens = [
Karsten Hopp 6ce2d7
        token for token in user_obj.tokens if token.project_id == project.id
Karsten Hopp 6ce2d7
    ]
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    connector = {
Karsten Hopp 6ce2d7
        "hook_token": project.hook_token,
Karsten Hopp 6ce2d7
        "api_tokens": [
Karsten Hopp 6ce2d7
            {"description": t.description, "id": t.id, "expired": t.expired}
Karsten Hopp 6ce2d7
            for t in user_project_tokens
Karsten Hopp 6ce2d7
        ],
Fabien Boucher dfb30c
    }
Fabien Boucher dfb30c
Fabien Boucher dfb30c
    return flask.jsonify({"connector": connector, "status": "ok"})
Fabien Boucher dfb30c
Fabien Boucher dfb30c
Pierre-Yves Chibon 473ccb
def _check_value(value):
Pierre-Yves Chibon 473ccb
    """ Convert the provided value into a boolean, an int or leave it as it.
Pierre-Yves Chibon 473ccb
    """
Pierre-Yves Chibon 473ccb
    if str(value).lower() in ["true"]:
Pierre-Yves Chibon 473ccb
        value = True
Pierre-Yves Chibon 473ccb
    elif str(value).lower() in ["false"]:
Pierre-Yves Chibon 473ccb
        value = True
Pierre-Yves Chibon ade066
    elif str(value).isdigit():
Pierre-Yves Chibon 473ccb
        value = int(value)
Pierre-Yves Chibon 473ccb
    return value
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
@API.route("/<repo>/options/update", methods=["POST"])</repo>
Pierre-Yves Chibon 473ccb
@API.route("/<namespace>/<repo>/options/update", methods=["POST"])</repo></namespace>
Pierre-Yves Chibon 473ccb
@API.route("/fork/<username>/<repo>/options/update", methods=["POST"])</repo></username>
Pierre-Yves Chibon 473ccb
@API.route(
Pierre-Yves Chibon 473ccb
    "/fork/<username>/<namespace>/<repo>/options/update", methods=["POST"]</repo></namespace></username>
Pierre-Yves Chibon 473ccb
)
Pierre-Yves Chibon 473ccb
@api_login_required(acls=["modify_project"])
Pierre-Yves Chibon 473ccb
@api_method
Pierre-Yves Chibon 473ccb
def api_modify_project_options(repo, username=None, namespace=None):
Pierre-Yves Chibon 473ccb
    """
Pierre-Yves Chibon 473ccb
    Update project options
Pierre-Yves Chibon 473ccb
    ----------------------
Pierre-Yves Chibon 473ccb
    Allow project admins to modify the options of a project.
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    ::
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
        POST /api/0/<repo>/options/update</repo>
Pierre-Yves Chibon 473ccb
        POST /api/0/<namespace>/<repo>/options/update</repo></namespace>
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    ::
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
        POST /api/0/fork/<username>/<repo>/options/update</repo></username>
Pierre-Yves Chibon 473ccb
        POST /api/0/fork/<username>/<namespace>/<repo>/options/update</repo></namespace></username>
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    Input
Pierre-Yves Chibon 473ccb
    ^^^^^
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    Simply specify the key/values you would like to set. Beware that if you
Pierre-Yves Chibon 473ccb
    do not specify in the request values that have been changed before they
Pierre-Yves Chibon 473ccb
    will go back to their default value.
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    Sample response
Pierre-Yves Chibon 473ccb
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    ::
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
        {
Pierre-Yves Chibon 473ccb
            'message': 'Edited successfully settings of repo: test',
Pierre-Yves Chibon 473ccb
            'status': 'ok'
Pierre-Yves Chibon 473ccb
        }
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    """
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    settings = {}
Pierre-Yves Chibon 473ccb
    for key in flask.request.form:
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
        settings[key] = _check_value(flask.request.form[key])
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    try:
Pierre-Yves Chibon 473ccb
        message = pagure.lib.query.update_project_settings(
Pierre-Yves Chibon 473ccb
            flask.g.session,
Pierre-Yves Chibon 473ccb
            repo=project,
Pierre-Yves Chibon 473ccb
            settings=settings,
Pierre-Yves Chibon 473ccb
            user=flask.g.fas_user.username,
Pierre-Yves Chibon 473ccb
            from_api=True,
Pierre-Yves Chibon 473ccb
        )
Pierre-Yves Chibon 473ccb
        flask.g.session.commit()
Pierre-Yves Chibon 473ccb
    except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon 473ccb
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 473ccb
            400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 473ccb
        )
Pierre-Yves Chibon 473ccb
    except SQLAlchemyError as err:  # pragma: no cover
Pierre-Yves Chibon 473ccb
        flask.g.session.rollback()
Pierre-Yves Chibon 473ccb
        _log.exception(err)
Pierre-Yves Chibon 473ccb
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Pierre-Yves Chibon 473ccb
Pierre-Yves Chibon 473ccb
    return flask.jsonify({"message": message, "status": "ok"})
Fabien Boucher 0bf7de
Fabien Boucher 41bac9
Fabien Boucher 1a6b69
@API.route("/<repo>/token/new", methods=["POST"])</repo>
Fabien Boucher 1a6b69
@API.route("/<namespace>/<repo>/token/new", methods=["POST"])</repo></namespace>
Fabien Boucher 1a6b69
@API.route("/fork/<username>/<repo>/token/new", methods=["POST"])</repo></username>
Fabien Boucher 032e3d
@API.route("/fork/<username>/<namespace>/<repo>/token/new", methods=["POST"])</repo></namespace></username>
Fabien Boucher 0bf7de
@api_login_required(acls=["modify_project"])
Fabien Boucher 0bf7de
@api_method
Fabien Boucher 0bf7de
def api_project_create_api_token(repo, namespace=None, username=None):
Fabien Boucher 0bf7de
    """
Fabien Boucher 0bf7de
    Create API project Token
Fabien Boucher 0bf7de
    ------------------------
Fabien Boucher 0bf7de
    Create a project token API for the caller user
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    This is restricted to project admins.
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    ::
Fabien Boucher 0bf7de
Fabien Boucher 1a6b69
        POST /api/0/<repo>/token/new</repo>
Fabien Boucher 1a6b69
        POST /api/0/<namespace>/<repo>/token/new</repo></namespace>
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    ::
Fabien Boucher 0bf7de
Fabien Boucher 1a6b69
        POST /api/0/fork/<username>/<repo>/token/new</repo></username>
Fabien Boucher 1a6b69
        POST /api/0/fork/<username>/<namespace>/<repo>/token/new</repo></namespace></username>
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    Input
Fabien Boucher 0bf7de
    ^^^^^
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    +------------------+---------+---------------+---------------------------+
Fabien Boucher 0bf7de
    | Key              | Type    | Optionality   | Description               |
Fabien Boucher 0bf7de
    +==================+=========+===============+===========================+
Fabien Boucher 53b792
    | ``description``  | String  | optional      | A string to specify the   |
Fabien Boucher 0bf7de
    |                  |         |               | description of the token  |
Fabien Boucher 0bf7de
    |                  |         |               |                           |
Fabien Boucher 0bf7de
    +------------------+---------+---------------+---------------------------+
Fabien Boucher 53b792
    | ``acls``         | List    | Mandatory     | The ACLs                  |
Fabien Boucher 0bf7de
    |                  |         |               |                           |
Fabien Boucher 0bf7de
    +------------------+---------+---------------+---------------------------+
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    Sample response
Fabien Boucher 0bf7de
    ^^^^^^^^^^^^^^^
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    ::
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
        {
Fabien Boucher 0bf7de
          "token": {
Fabien Boucher 0bf7de
            "description": "My foo token",
Fabien Boucher 0bf7de
            "id": "aaabbbcccfootoken",
Fabien Boucher 0bf7de
          },
Fabien Boucher 0bf7de
        }
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    """
Fabien Boucher 0bf7de
    output = {}
Fabien Boucher 0bf7de
Pierre-Yves Chibon 01d0f2
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 01d0f2
    _check_token(project, project_token=False)
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    authorized_users = [project.user.username]
Fabien Boucher 0bf7de
    authorized_users.extend(
Pierre-Yves Chibon 3b2728
        [user.user for user in project.access_users["admin"]]
Pierre-Yves Chibon 3b2728
    )
Fabien Boucher 0bf7de
    if flask.g.fas_user.user not in authorized_users:
Fabien Boucher 0bf7de
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 3b2728
            401, error_code=APIERROR.ENOTHIGHENOUGH
Pierre-Yves Chibon 3b2728
        )
Fabien Boucher 0bf7de
Fabien Boucher 579887
    authorized_acls = pagure_config.get("USER_ACLS", [])
Pierre-Yves Chibon 3b2728
    form = pagure.forms.NewTokenForm(csrf_enabled=False, sacls=authorized_acls)
Fabien Boucher 53b792
    if form.validate_on_submit():
Fabien Boucher 53b792
        acls = form.acls.data
Fabien Boucher 53b792
        description = form.description.data
Fabien Boucher 53b792
    else:
Fabien Boucher 0bf7de
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 3b2728
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
Pierre-Yves Chibon 3b2728
        )
Fabien Boucher 0bf7de
Fabien Boucher 579887
    token = pagure.lib.query.add_token_to_user(
Pierre-Yves Chibon 3b2728
        flask.g.session, project, acls, flask.g.fas_user.user, description
Pierre-Yves Chibon 3b2728
    )
Pierre-Yves Chibon 3b2728
    output = {"token": {"description": token.description, "id": token.id}}
Fabien Boucher 0bf7de
Fabien Boucher 0bf7de
    jsonout = flask.jsonify(output)
Fabien Boucher 0bf7de
    return jsonout
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
@API.route("/<repo>/blockuser", methods=["POST"])</repo>
Pierre-Yves Chibon 4b7c3e
@API.route("/<namespace>/<repo>/blockuser", methods=["POST"])</repo></namespace>
Pierre-Yves Chibon 4b7c3e
@API.route("/fork/<username>/<repo>/blockuser", methods=["POST"])</repo></username>
Pierre-Yves Chibon 4b7c3e
@API.route("/fork/<username>/<namespace>/<repo>/blockuser", methods=["POST"])</repo></namespace></username>
Pierre-Yves Chibon 4b7c3e
@api_login_required(acls=["modify_project"])
Pierre-Yves Chibon 4b7c3e
@api_method
Pierre-Yves Chibon 4b7c3e
def api_project_block_user(repo, namespace=None, username=None):
Pierre-Yves Chibon 4b7c3e
    """
Pierre-Yves Chibon 4b7c3e
    Block an user from a project
Pierre-Yves Chibon 4b7c3e
    ----------------------------
Pierre-Yves Chibon 4b7c3e
    Block an user from interacting with the project
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    This is restricted to project admins.
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    ::
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
        POST /api/0/<repo>/blockuser</repo>
Pierre-Yves Chibon 4b7c3e
        POST /api/0/<namespace>/<repo>/blockuser</repo></namespace>
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    ::
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
        POST /api/0/fork/<username>/<repo>/blockuser</repo></username>
Pierre-Yves Chibon 4b7c3e
        POST /api/0/fork/<username>/<namespace>/<repo>/blockuser</repo></namespace></username>
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    Input
Pierre-Yves Chibon 4b7c3e
    ^^^^^
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    +------------------+---------+---------------+---------------------------+
Pierre-Yves Chibon 4b7c3e
    | Key              | Type    | Optionality   | Description               |
Pierre-Yves Chibon 4b7c3e
    +==================+=========+===============+===========================+
Pierre-Yves Chibon 4b7c3e
    | ``username``     | String  | optional      | The username of the user  |
Pierre-Yves Chibon 4b7c3e
    |                  |         |               | to block on this project  |
Pierre-Yves Chibon 4b7c3e
    +------------------+---------+---------------+---------------------------+
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    Beware that this API endpoint updates **all** the users blocked in the
Pierre-Yves Chibon 4b7c3e
    project, so if you are updating this list, do not submit just one username,
Pierre-Yves Chibon 4b7c3e
    submit the updated list.
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    Sample response
Pierre-Yves Chibon 4b7c3e
    ^^^^^^^^^^^^^^^
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    ::
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
        {"message": "User(s) blocked"}
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    """
Pierre-Yves Chibon 4b7c3e
    output = {}
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    project = _get_repo(repo, username, namespace)
Pierre-Yves Chibon 4b7c3e
    _check_token(project)
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    authorized_users = [project.user.username]
Pierre-Yves Chibon 4b7c3e
    authorized_users.extend(
Pierre-Yves Chibon 4b7c3e
        [user.user for user in project.access_users["admin"]]
Pierre-Yves Chibon 4b7c3e
    )
Pierre-Yves Chibon 4b7c3e
    if flask.g.fas_user.username not in authorized_users:
Pierre-Yves Chibon 4b7c3e
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 4b7c3e
            401, error_code=APIERROR.ENOTHIGHENOUGH
Pierre-Yves Chibon 4b7c3e
        )
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    usernames = flask.request.form.getlist("username")
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    try:
Pierre-Yves Chibon 4b7c3e
        users = set()
Pierre-Yves Chibon 4b7c3e
        for user in usernames:
Pierre-Yves Chibon 4b7c3e
            user = user.strip()
Pierre-Yves Chibon 4b7c3e
            if user:
Pierre-Yves Chibon 4b7c3e
                pagure.lib.query.get_user(flask.g.session, user)
Pierre-Yves Chibon 4b7c3e
                users.add(user)
Pierre-Yves Chibon 4b7c3e
        project.block_users = list(users)
Pierre-Yves Chibon 4b7c3e
        flask.g.session.add(project)
Pierre-Yves Chibon 4b7c3e
        flask.g.session.commit()
Pierre-Yves Chibon 4b7c3e
        output = {"message": "User(s) blocked"}
Pierre-Yves Chibon 4b7c3e
    except pagure.exceptions.PagureException as err:
Pierre-Yves Chibon 4b7c3e
        raise pagure.exceptions.APIError(
Pierre-Yves Chibon 4b7c3e
            400, error_code=APIERROR.ENOCODE, error=str(err)
Pierre-Yves Chibon 4b7c3e
        )
Pierre-Yves Chibon 4b7c3e
    except SQLAlchemyError as err:  # pragma: no cover
Pierre-Yves Chibon 4b7c3e
        flask.g.session.rollback()
Pierre-Yves Chibon 4b7c3e
        _log.exception(err)
Pierre-Yves Chibon 4b7c3e
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
Pierre-Yves Chibon 4b7c3e
Pierre-Yves Chibon 4b7c3e
    jsonout = flask.jsonify(output)
Pierre-Yves Chibon 4b7c3e
    return jsonout