#2266 frontend: remove APIv2 code
Merged 2 years ago by praiskup. Opened 2 years ago by frostyx.
copr/ frostyx/copr drop-avpiv2-frontend  into  main

@@ -186,10 +186,6 @@ 

  app.jinja_env.trim_blocks = True

  app.jinja_env.lstrip_blocks = True

  

- from coprs.rest_api import rest_api_bp, register_api_error_handler, URL_PREFIX

- register_api_error_handler(app)

- app.register_blueprint(rest_api_bp, url_prefix=URL_PREFIX)

- # register_api(app, db)

  setup_profiler(app, profiler_enabled)

  

  from flask_sqlalchemy import models_committed

@@ -403,14 +403,6 @@ 

          return cls.get_multiple().filter(models.Build.copr == copr)

  

      @classmethod

-     def get_multiple_by_user(cls, user):

-         """ Get collection of builds in copr sorted by build_id descending

-         form the copr belonging to `user`

-         """

-         return cls.get_multiple().join(models.Build.copr).filter(

-             models.Copr.user == user)

- 

-     @classmethod

      def get_copr_builds_list(cls, copr, dirname=None):

          query = models.Build.query.filter(models.Build.copr_id==copr.id)

          if dirname:
@@ -1381,24 +1373,6 @@ 

          return query

  

      @classmethod

-     def get_by_results(cls, **kwargs):

-         """

-         Query all `BuildChroot` instances whose `results` corresponds with the

-         specified `kwargs`.

- 

-         Supported parameter names:

- 

-             name, epoch, version, release, arch

- 

-         Example usage:

- 

-             cls.get_by_results(name="hello")

-             cls.get_by_results(name="foo", arch="x86_64")

-         """

-         return (models.BuildChroot.query

-                 .filter(models.BuildChroot.results.any(**kwargs)))

- 

-     @classmethod

      def filter_by_build_id(cls, query, build_id):

          return query.filter(models.Build.id == build_id)

  

@@ -146,10 +146,6 @@ 

          return query.filter(models.User.username == username)

  

      @classmethod

-     def filter_by_name(cls, query, name):

-         return query.filter(models.Copr.name == name)

- 

-     @classmethod

      def filter_by_user_name(cls, query, username):

          # should be already joined with the User table

          return query.filter(models.User.username == username)

@@ -1,14 +1,6 @@ 

  # coding: utf-8

  import time

  

- def slice_query(query, limit=100, offset=0):

-     """

-     :param Query query:

-     :param int limit:

-     :param int offset:

-     :rtype: Query

-     """

-     return query.limit(limit).offset(offset)

  

  def get_graph_parameters(type):

      if type == "10min":

@@ -1048,10 +1048,6 @@ 

      _cached_status_set = None

  

      @property

-     def user_name(self):

-         return self.user.name

- 

-     @property

      def group_name(self):

          return self.copr.group.name

  
@@ -1190,16 +1186,6 @@ 

          return helpers.BuildSourceEnum(self.source_type)

  

      @property

-     def source_metadata(self):

-         if self.source_json is None:

-             return None

- 

-         try:

-             return json.loads(self.source_json)

-         except (TypeError, ValueError):

-             return None

- 

-     @property

      def chroot_states(self):

          return list(map(lambda chroot: chroot.status, self.build_chroots))

  
@@ -1662,13 +1648,6 @@ 

              return zlib.decompress(self.comps_zlib).decode("utf-8")

  

      @property

-     def comps_len(self):

-         if self.comps_zlib:

-             return len(zlib.decompress(self.comps_zlib))

-         else:

-             return 0

- 

-     @property

      def name(self):

          return self.mock_chroot.name

  

@@ -1,94 +0,0 @@ 

- # coding: utf-8

- import json

- from flask import Response, url_for, Blueprint, make_response

- from flask_restful import Resource, Api

- 

- from coprs.exceptions import InsufficientRightsException

- 

- from coprs.rest_api.exceptions import ApiError

- from coprs.rest_api.resources.build import BuildListR, BuildR

- from coprs.rest_api.resources.build_task import BuildTaskListR, BuildTaskR

- from coprs.rest_api.resources.mock_chroot import MockChrootListR, MockChrootR

- from coprs.rest_api.resources.project import ProjectListR, ProjectR

- from coprs.rest_api.resources.project_chroot import ProjectChrootListR, ProjectChrootR

- 

- URL_PREFIX = "/api_2"

- 

- 

- class RootR(Resource):

-     @classmethod

-     def get(cls):

-         return {

-             "_links": {

-                 "self": {"href": url_for(".rootr")},

-                 "projects": {

-                     "href": url_for(".projectlistr"),

-                     "query_params": [

-                         {

-                             "name": u"owner",

-                             "type": u"string",

-                             "description": u"Select only project owned by the user"

-                         }

-                     ]

-                 },

-                 "mock_chroots": {"href": url_for(".mockchrootlistr")},

-                 "builds": {"href": url_for(".buildlistr")},

-                 "build_tasks": {"href": url_for(".buildtasklistr")},

-             }

-         }

- 

- 

- class MyApi(Api):

-     # flask-restfull error handling quite buggy right now

-     def error_router(self, original_handler, e):

-         return original_handler(e)

- #     # def handle_error(self, e):

- #     #

- #     #     if isinstance(e, sqlalchemy.orm.exc.NoResultFound):

- #     #         return self.make_response(str(e), 404)

- #     #

- #     #

- #     #     super(MyApi, self).handle_error(e)

- 

- 

- # def register_api(app, db):

- 

- 

- rest_api_bp = Blueprint("rest_api_bp", __name__)

- api = MyApi(rest_api_bp, catch_all_404s=True)

- 

- api.add_resource(RootR, "/")

- api.add_resource(ProjectListR, "/projects")

- api.add_resource(ProjectR, "/projects/<int:project_id>")

- 

- api.add_resource(MockChrootListR, "/mock_chroots")

- api.add_resource(MockChrootR, "/mock_chroots/<name>")

- 

- api.add_resource(BuildListR, "/builds")

- api.add_resource(BuildR, "/builds/<int:build_id>")

- 

- api.add_resource(ProjectChrootListR, "/projects/<int:project_id>/chroots")

- api.add_resource(ProjectChrootR, "/projects/<int:project_id>/chroots/<name>")

- 

- 

- api.add_resource(BuildTaskListR, "/build_tasks")

- # todo: add redirect from /build_tasks/<int:build_id> -> /build_tasks?build_id=<build_id>

- api.add_resource(BuildTaskR, "/build_tasks/<int:build_id>/<name>")

- 

- 

- def register_api_error_handler(app):

-     @app.errorhandler(ApiError)

-     def handle_api_error(error):

-         """

-         :param ApiError error:

-         """

- 

-         content = {

-             "message": error.msg,

-         }

-         if error.data:

-             content["data"] = error.data

- 

-         response = make_response(json.dumps(content), error.code)

-         response.headers["Content-Type"] = "application/json"

-         return response

@@ -1,148 +0,0 @@ 

- # coding: utf-8

- import base64

- import datetime

- import functools

- 

- from flask import url_for

- import flask

- 

- from ..logic.users_logic import UsersLogic

- from ..logic.builds_logic import BuildsLogic

- from ..logic.coprs_logic import CoprsLogic

- from ..rest_api.schemas import BuildTaskSchema

- from ..rest_api.util import mm_serialize_one, get_one_safe

- 

- from .exceptions import AuthFailed

- from .schemas import CoprChrootSchema, BuildSchema, ProjectSchema

- from .util import mm_serialize_one

- from coprs import app

- 

- 

- def render_copr_chroot(chroot):

-     return {

-         "chroot": mm_serialize_one(CoprChrootSchema, chroot),

-         "_links": {

-             "project": {"href": url_for(".projectr", project_id=chroot.copr.id)},

-             "self": {"href": url_for(".projectchrootr",

-                                      project_id=chroot.copr.id,

-                                      name=chroot.name)},

-         }

-     }

- 

- 

- def render_build(build, self_params=None):

-     if self_params is None:

-         self_params = {}

-     return {

-         "build": mm_serialize_one(BuildSchema, build),

-         "_links": {

-             "self": {"href": url_for(".buildr", build_id=build.id, **self_params)},

-             "project": {"href": url_for(".projectr", project_id=build.copr_id)},

-             "build_tasks": {"href": url_for(".buildtasklistr", build_id=build.id)}

-         }

-     }

- 

- 

- def render_project(project, self_params=None):

-     """

-     :param Copr project:

-     """

-     if self_params is None:

-         self_params = {}

- 

-     return {

-         "project": mm_serialize_one(ProjectSchema, project),

-         "_links": {

-             "self": {"href": url_for(".projectr", project_id=project.id, **self_params)},

-             "builds": {"href": url_for(".buildlistr", project_id=project.id)},

-             "chroots": {"href": url_for(".projectchrootlistr", project_id=project.id)},

-             "build_tasks": {"href":url_for(".buildtasklistr", project_id=project.id) }

-         },

-     }

- 

- 

- def render_build_task(chroot):

-     """

-     :type chroot: BuildChroot

-     """

-     return {

-         "build_task": mm_serialize_one(BuildTaskSchema, chroot),

-         "_links": {

-             "project": {"href": url_for(".projectr", project_id=chroot.build.copr_id)},

-             "build": {"href": url_for(".buildr", build_id=chroot.build_id)},

-             "self": {"href": url_for(".buildtaskr",

-                                      build_id=chroot.build.id,

-                                      name=chroot.name)},

-         }

-     }

- 

- 

- def rest_api_auth_required(f):

-     # todo: move to common.py and test this

-     @functools.wraps(f)

-     def decorated_function(*args, **kwargs):

-         token = None

-         api_login = None

-         try:

-             if "Authorization" in flask.request.headers:

-                 base64string = flask.request.headers["Authorization"]

-                 base64string = base64string.split()[1].strip()

-                 userstring = base64.b64decode(base64string)

-                 (api_login, token) = userstring.decode("utf-8").split(":")

-         except Exception:

-             app.logger.exception("Failed to get auth token from headers")

-             api_login = token = None

- 

-         token_auth = False

-         if token and api_login:

-             user = UsersLogic.get_by_api_login(api_login).first()

-             if (user and user.api_token == token and

-                     user.api_token_expiration >= datetime.date.today()):

- 

-                 token_auth = True

-                 flask.g.user = user

-         if not token_auth:

-             message = (

-                 "Login invalid/expired. "

-                 "Please visit {0}/api "

-                 "get or renew your API token.")

- 

-             raise AuthFailed(message.format(app.config["PUBLIC_COPR_HOSTNAME"]))

-         return f(*args, **kwargs)

-     return decorated_function

- 

- 

- def get_project_safe(project_id):

-     """

-     :param int project_id:

-     :rtype: Copr

-     """

-     return get_one_safe(

-         CoprsLogic.get_by_id(project_id),

-         msg="Project with id `{}` not found".format(project_id),

-         data={"project_id": project_id}

-     )

- 

- 

- def get_build_safe(build_id):

-     """

-     :param int build_id:

-     :rtype: Build

-     """

-     return get_one_safe(

-         BuildsLogic.get(build_id),

-         msg="Build with id `{}` not found".format(build_id),

-         data={"build_id": build_id}

-     )

- 

- 

- def get_user_safe(username):

-     """

-     :param str username:

-     :rtype: User

-     """

-     return get_one_safe(

-         UsersLogic.get(username),

-         msg="User `{}` doesn't have any project".format(username),

-         data={"username": username}

-     )

@@ -1,67 +0,0 @@ 

- # coding: utf-8

- 

- class ApiError(Exception):

-     def __init__(self, code, msg, data=None, **kwargs):

-         super(ApiError, self).__init__(**kwargs)

- 

-         self.code = code

-         self.data = data

-         self.msg = msg

- 

-         self.headers = kwargs.get("headers", {})

- 

-     def __str__(self):

-         return str(self.data)

- 

- 

- class AuthFailed(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Authorization failed"

-         super(AuthFailed, self).__init__(401, msg=msg, data=data, **kwargs)

-         self.headers["Authorization"] = "Basic"

- 

- 

- class AccessForbidden(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Access forbidden"

-         super(AccessForbidden, self).__init__(403, msg=msg, data=data, **kwargs)

- 

- 

- class ObjectNotFoundError(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Requested object wasn't found"

-         super(ObjectNotFoundError, self).__init__(404, msg=msg, data=data, **kwargs)

- 

- 

- class ObjectAlreadyExists(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Operational error, trying to create existing object"

- 

-         super(ObjectAlreadyExists, self).__init__(409, msg=msg, data=data, **kwargs)

- 

- 

- class MalformedRequest(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Given request contains errors or couldn't be executed in the current context"

- 

-         super(MalformedRequest, self).__init__(400, msg=msg, data=data, **kwargs)

- 

- 

- class CannotProcessRequest(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Cannot process given request"

- 

-         super(CannotProcessRequest, self).__init__(400, msg=msg, data=data, **kwargs)

- 

- 

- class ServerError(ApiError):

-     def __init__(self, msg=None, data=None, **kwargs):

-         if msg is None:

-             msg = "Unhandled server error, please contact site administrator"

-         super(ServerError, self).__init__(500, msg=msg, data=data, **kwargs)

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

- # coding: utf-8

@@ -1,228 +0,0 @@ 

- # coding: utf-8

- 

- import flask

- from flask import url_for, make_response

- from flask_restful import Resource

- 

- from ... import db

- from ...exceptions import (

-     ActionInProgressException,

-     ConflictingRequest,

-     InsufficientRightsException,

- )

- from ...logic.builds_logic import BuildsLogic

- from ..common import get_project_safe

- from ..exceptions import MalformedRequest, CannotProcessRequest, AccessForbidden

- from ..common import render_build, rest_api_auth_required, render_build_task, get_build_safe, get_user_safe

- from ..schemas import BuildSchema, BuildCreateSchema, BuildCreateFromUrlSchema

- from ..util import mm_deserialize, get_request_parser, arg_bool

- 

- 

- class BuildListR(Resource):

- 

-     @classmethod

-     def get(cls):

- 

-         parser = get_request_parser()

- 

-         parser.add_argument('owner', type=str,)

-         parser.add_argument('project_id', type=int)

-         parser.add_argument('group', type=str)

- 

-         parser.add_argument('limit', type=int)

-         parser.add_argument('offset', type=int)

- 

-         parser.add_argument('is_finished', type=arg_bool)

-         # parser.add_argument('package', type=str)

- 

-         req_args = parser.parse_args()

- 

-         if req_args["project_id"] is not None:

-             project = get_project_safe(req_args["project_id"])

-             query = BuildsLogic.get_multiple_by_copr(project)

-         elif req_args["owner"] is not None:

-             user = get_user_safe(req_args["owner"])

-             query = BuildsLogic.get_multiple_by_user(user)

-         else:

-             query = BuildsLogic.get_multiple()

- 

-         if req_args["group"]:

-             query = BuildsLogic.filter_by_group_name(query, req_args["group"])

- 

-         if req_args["is_finished"] is not None:

-             is_finished = req_args["is_finished"]

-             query = BuildsLogic.filter_is_finished(query, is_finished)

- 

-         if req_args["limit"] is not None:

-             limit = req_args["limit"]

-             if limit <= 0 or limit > 100:

-                 limit = 100

-         else:

-             limit = 100

- 

-         query = query.limit(limit)

- 

-         if req_args["offset"] is not None:

-             query = query.offset(req_args["offset"])

- 

-         builds = query.all()

- 

-         self_params = dict(req_args)

-         self_params["limit"] = limit

-         return {

-             "builds": [

-                 render_build(build) for build in builds

-             ],

-             "_links": {

-                 "self": {"href": url_for(".buildlistr", **self_params)},

-             },

-         }

- 

-     @staticmethod

-     def handle_post_json(req):

-         """

-         :return: if of the created build or raise Exception

-         """

-         build_params = mm_deserialize(BuildCreateFromUrlSchema(), req.data.decode("utf-8"))

-         project = get_project_safe(build_params["project_id"])

- 

-         chroot_names = build_params.pop("chroots")

-         srpm_url = build_params.pop("srpm_url")

-         try:

-             build = BuildsLogic.create_new_from_url(

-                 flask.g.user, project,

-                 url=srpm_url,

-                 chroot_names=chroot_names,

-                 **build_params

-             )

-             db.session.commit()

-         except ActionInProgressException as err:

-             db.session.rollback()

-             raise CannotProcessRequest("Cannot create new build due to: {}"

-                                        .format(err))

-         except InsufficientRightsException as err:

-             db.session.rollback()

-             raise AccessForbidden("User {} cannot create build in project {}: {}"

-                                   .format(flask.g.user.username,

-                                           project.full_name, err))

-         return build.id

- 

-     @staticmethod

-     def handle_post_multipart(req):

-         """

-         :return: if of the created build or raise Exception

-         """

-         try:

-             metadata = req.form["metadata"]

-         except KeyError:

-             raise MalformedRequest("Missing build metadata in the request")

- 

-         if "srpm" not in req.files:

-             raise MalformedRequest("Missing srpm file in the request")

-         srpm_handle = req.files["srpm"]

- 

-         build_params = mm_deserialize(BuildCreateSchema(), metadata)

-         project_id = build_params["project_id"]

- 

-         project = get_project_safe(project_id)

- 

-         chroot_names = build_params.pop("chroots")

-         try:

-             build = BuildsLogic.create_new_from_upload(

-                 flask.g.user, project,

-                 f_uploader=lambda path: srpm_handle.save(path),

-                 orig_filename=srpm_handle.filename,

-                 chroot_names=chroot_names,

-                 **build_params

-             )

-             db.session.commit()

-         except ActionInProgressException as err:

-             db.session.rollback()

-             raise CannotProcessRequest("Cannot create new build due to: {}"

-                                        .format(err))

-         except InsufficientRightsException as err:

-             db.session.rollback()

-             raise AccessForbidden("User {} cannon create build in project {}: {}"

-                                   .format(flask.g.user.username,

-                                           project.full_name, err))

- 

-         return build.id

- 

-     @rest_api_auth_required

-     def post(self):

- 

-         req = flask.request

-         if req.content_type is None:

-             raise MalformedRequest("Got request without content type header")

- 

-         if "application/json" in req.content_type:

-             build_id = self.handle_post_json(req)

-         elif "multipart/form-data" in req.content_type:

-             build_id = self.handle_post_multipart(req)

-         else:

-             raise MalformedRequest("Got unexpected content type: {}"

-                                    .format(req.content_type))

-         resp = make_response("", 201)

-         resp.headers["Location"] = url_for(".buildr", build_id=build_id)

- 

-         return resp

- 

- 

- class BuildR(Resource):

- 

-     @classmethod

-     def get(cls, build_id):

-         parser = get_request_parser()

-         parser.add_argument('show_build_tasks', type=arg_bool, default=False)

-         req_args = parser.parse_args()

- 

-         build = get_build_safe(build_id)

- 

-         self_params = {}

-         if req_args["show_build_tasks"]:

-             self_params["show_build_tasks"] = req_args["show_build_tasks"]

- 

-         result = render_build(build, self_params)

-         if req_args["show_build_tasks"]:

-             result["build_tasks"] = [

-                 render_build_task(chroot)

-                 for chroot in build.build_chroots

-             ]

- 

-         return result

- 

-     @classmethod

-     @rest_api_auth_required

-     def delete(cls, build_id):

-         build = get_build_safe(build_id)

-         try:

-             BuildsLogic.delete_build(flask.g.user, build)

-             db.session.commit()

-         except ActionInProgressException as err:

-             db.session.rollback()

-             raise CannotProcessRequest("Cannot delete build due to: {}"

-                                        .format(err))

-         except InsufficientRightsException as err:

-             raise AccessForbidden("Failed to delete build: {}".format(err))

- 

-         return "", 204

- 

-     @classmethod

-     @rest_api_auth_required

-     def put(self, build_id):

-         build = get_build_safe(build_id)

-         build_dict = mm_deserialize(BuildSchema(), flask.request.data.decode("utf-8"))

-         try:

-             if not build.canceled and build_dict["state"] == "canceled":

-                 BuildsLogic.cancel_build(flask.g.user, build)

-                 db.session.commit()

-         except (ActionInProgressException, ConflictingRequest) as err:

-             db.session.rollback()

-             raise CannotProcessRequest("Cannot update build due to: {}"

-                                        .format(err))

-         except InsufficientRightsException as err:

-             raise AccessForbidden("Failed to update build: {}".format(err))

- 

-         resp = make_response("", 201)

-         resp.headers["Location"] = url_for(".buildr", build_id=build_id)

-         return resp

@@ -1,103 +0,0 @@ 

- # coding: utf-8

- 

- from flask import url_for

- from flask_restful import Resource

- 

- from copr_common.enums import StatusEnum

- from coprs.rest_api.common import render_build_task

- from ...exceptions import MalformedArgumentException

- from ...logic.builds_logic import BuildChrootsLogic

- from ..exceptions import MalformedRequest

- from ..util import get_one_safe, get_request_parser

- 

- 

- # todo: add redirect from /build_tasks/<int:build_id> -> /build_tasks?build_id=<build_id>

- # class BuildTaskListRedirectBuildIdR(Resource):

- #     def get(self, build_id):

- #         resp = make_response("", 302)

- #         resp.headers["Location"] ==  url_for(".buildtasklistr", build_id=build_id)

- 

- 

- class BuildTaskListR(Resource):

-     state_choices = StatusEnum.vals.keys()

- 

-     def get(self):

- 

-         parser = get_request_parser()

- 

-         parser.add_argument('owner', type=str,)

-         parser.add_argument('project_id', type=int)

-         parser.add_argument('build_id', type=int)

-         parser.add_argument('group', type=str)

- 

-         parser.add_argument('limit', type=int)

-         parser.add_argument('offset', type=int)

- 

-         parser.add_argument(

-             'state', type=str, choices=self.state_choices,

-             help=u"allowed states: {}".format(" ".join(self.state_choices)))

- 

-         req_args = parser.parse_args()

- 

-         self_params = dict(req_args)

- 

-         query = BuildChrootsLogic.get_multiply()

-         if self_params.get("build_id") is not None:

-             query = BuildChrootsLogic.filter_by_build_id(

-                 query, self_params["build_id"])

-         elif self_params.get("project_id") is not None:

-             query = BuildChrootsLogic.filter_by_project_id(

-                 query, self_params["project_id"])

-         elif self_params.get("owner") is not None:

-             query = BuildChrootsLogic.filter_by_project_user_name(

-                 query, self_params["owner"])

-         elif self_params.get("group") is not None:

-             query = BuildChrootsLogic.filter_by_group_name(query, req_args["group"])

- 

-         state = self_params.get("state")

-         if state:

-             query = BuildChrootsLogic.filter_by_state(query, state)

- 

-         if req_args["limit"] is not None:

-             limit = req_args["limit"]

-             if limit <= 0 or limit > 100:

-                 limit = 100

-         else:

-             limit = 100

-         self_params["limit"] = limit

-         query = query.limit(limit)

- 

-         if "offset" in self_params is not None:

-             query = query.offset(self_params["offset"])

- 

-         build_chroots = query.all()

-         return {

-             "build_tasks": [

-                 render_build_task(chroot)

-                 for chroot in build_chroots

-             ],

-             "_links": {

-                 "self": {"href": url_for(".buildtasklistr", **self_params)}

-             }

-         }

- 

- 

- class BuildTaskR(Resource):

- 

-     @staticmethod

-     def _get_chroot_safe(build_id, name):

-         try:

-             chroot = get_one_safe(

-                 BuildChrootsLogic.get_by_build_id_and_name(build_id, name),

-                 "Build task {} for build {} not found"

-             )

-         except MalformedArgumentException as err:

-             raise MalformedRequest("Bad mock chroot name: {}".format(err))

-         return chroot

- 

-     def get(self, build_id, name):

-         chroot = self._get_chroot_safe(build_id, name)

-         return render_build_task(chroot)

- 

-     # todo: add put method: allows only to pass status: cancelled to cancel build

- 

@@ -1,52 +0,0 @@ 

- # coding: utf-8

- 

- from flask import url_for

- from flask_restful import Resource

- 

- from ...logic.coprs_logic import MockChrootsLogic

- 

- from ..schemas import MockChrootSchema

- from ..util import get_one_safe, get_request_parser, arg_bool, mm_serialize_one

- 

- 

- def render_mock_chroot(chroot):

-     return {

-         "chroot": mm_serialize_one(MockChrootSchema, chroot),

-         "_links": {

-             "self": {"href": url_for(".mockchrootr", name=chroot.name)},

-         },

-     }

- 

- 

- class MockChrootListR(Resource):

- 

-     @classmethod

-     def get(cls):

-         parser = get_request_parser()

-         parser.add_argument('active_only', type=arg_bool)

-         req_args = parser.parse_args()

-         active_only = False

-         if req_args["active_only"]:

-             active_only = True

- 

-         chroots = MockChrootsLogic.get_multiple(active_only=active_only).all()

- 

-         self_extra = {}

-         if active_only:

-             self_extra["active_only"] = active_only

-         return {

-             "_links": {

-                 "self": {"href": url_for(".mockchrootlistr", **self_extra)},

-             },

-             "chroots": [

-                 render_mock_chroot(chroot)

-                 for chroot in chroots

-             ]

-         }

- 

- 

- class MockChrootR(Resource):

-     @classmethod

-     def get(cls, name):

-         chroot = get_one_safe(MockChrootsLogic.get_from_name(name))

-         return render_mock_chroot(chroot)

@@ -1,174 +0,0 @@ 

- import flask

- from flask import url_for, make_response

- from flask_restful import Resource

- 

- from ... import db

- from ...logic.builds_logic import BuildsLogic

- from ...logic.complex_logic import ComplexLogic

- from ...logic.helpers import slice_query

- from ...logic.coprs_logic import CoprsLogic

- from ...exceptions import ActionInProgressException, InsufficientRightsException

- 

- from ...exceptions import DuplicateException

- 

- from ..common import rest_api_auth_required, render_copr_chroot, render_build, render_project, get_project_safe

- from ..schemas import ProjectSchema, ProjectCreateSchema

- from ..exceptions import ObjectAlreadyExists, CannotProcessRequest, AccessForbidden

- from ..util import mm_deserialize, get_request_parser, arg_bool

- 

- 

- class ProjectListR(Resource):

- 

-     @rest_api_auth_required

-     def post(self):

-         """

-         Creates new copr

-         """

-         user = flask.g.user

-         req = mm_deserialize(ProjectCreateSchema(), flask.request.data.decode("utf-8"))

-         name = req.pop("name")

- 

-         selected_chroots = req.pop("chroots", None)

- 

-         group_name = req.pop("group", None)

-         if group_name:

-             group = ComplexLogic.get_group_by_name_safe(group_name)

-         else:

-             group = None

- 

-         try:

-             project = CoprsLogic.add(

-                 user=user, check_for_duplicates=True,

-                 name=name,

-                 selected_chroots=selected_chroots,

-                 group=group,

-                 **req

-             )

- 

-             db.session.commit()

-         except DuplicateException as error:

-             raise ObjectAlreadyExists(msg=str(error))

- 

-         resp = make_response("", 201)

-         resp.headers["Location"] = url_for(".projectr", project_id=project.id)

-         return resp

- 

-     def get(self):

-         parser = get_request_parser()

-         parser.add_argument('owner', type=str)

-         parser.add_argument('group', type=str)

-         parser.add_argument('name', type=str)

-         parser.add_argument('limit', type=int)

-         parser.add_argument('offset', type=int)

-         parser.add_argument('search_query', type=str)

- 

-         req_args = parser.parse_args()

- 

-         if req_args["search_query"]:

-             ownername = req_args["owner"]

-             if req_args["group"]:

-                 ownername = "@" + req_args["group"]

- 

-             query = CoprsLogic.get_multiple_fulltext(

-                 req_args["search_query"],

-                 projectname=req_args["name"],

-                 ownername=ownername)

- 

-         else:

-             query = CoprsLogic.get_multiple(flask.g.user)

-             if req_args["owner"]:

-                 query = CoprsLogic.filter_by_user_name(query, req_args["owner"])

- 

-             if req_args["group"]:

-                 query = CoprsLogic.filter_by_group_name(query, req_args["group"])

- 

-             if req_args["name"]:

-                 query = CoprsLogic.filter_by_name(query, req_args["name"])

- 

-         query = CoprsLogic.set_query_order(query)

- 

-         limit = 100

-         offset = 0

-         if req_args["limit"]:

-             limit = req_args["limit"]

-         if req_args["offset"]:

-             offset = req_args["offset"]

- 

-         query = slice_query(query, limit, offset)

-         coprs_list = query.all()

- 

-         result_dict = {

-             "_links": {

-                 "self": {"href": url_for(".projectlistr", **req_args)}

-             },

-             "projects": [render_project(copr) for copr in coprs_list],

-         }

- 

-         return result_dict

- 

- 

- class ProjectR(Resource):

- 

-     @rest_api_auth_required

-     def delete(self, project_id):

-         project = get_project_safe(project_id)

- 

-         try:

-             ComplexLogic.delete_copr(project)

-             db.session.commit()

-         except ActionInProgressException as err:

-             raise CannotProcessRequest(str(err))

-         except InsufficientRightsException as err:

-             raise AccessForbidden(str(err))

- 

-         return None, 204

- 

-     def get(self, project_id):

-         parser = get_request_parser()

-         parser.add_argument('show_builds', type=arg_bool, default=False)

-         parser.add_argument('show_chroots', type=arg_bool, default=False)

-         req_args = parser.parse_args()

- 

-         project = get_project_safe(project_id)

-         self_params = {}

-         if req_args["show_builds"]:

-             self_params["show_builds"] = req_args["show_builds"]

-         if req_args["show_chroots"]:

-             self_params["show_chroots"] = req_args["show_chroots"]

- 

-         answer = render_project(project, self_params)

- 

-         if req_args["show_builds"]:

-             answer["project_builds"] = [

-                 render_build(build)

-                 for build in BuildsLogic.get_multiple_by_copr(project).all()

-             ]

- 

-         if req_args["show_chroots"]:

-             answer["project_chroots"] = [

-                 render_copr_chroot(chroot)

-                 for chroot in project.active_copr_chroots

-             ]

- 

-         return answer

- 

-     @rest_api_auth_required

-     def put(self, project_id):

-         """

-         Modifies project by replacement of provided fields

-         """

-         project = get_project_safe(project_id)

- 

-         project_dict = mm_deserialize(ProjectSchema(), flask.request.data.decode("utf-8"))

- 

-         for k, v in project_dict.items():

-             setattr(project, k, v)

- 

-         try:

-             CoprsLogic.update(flask.g.user, project)

-             db.session.commit()

-         except InsufficientRightsException as err:

-             raise AccessForbidden(str(err))

- 

-         return "", 204

- 

@@ -1,114 +0,0 @@ 

- # coding: utf-8

- 

- import flask

- from flask import url_for, make_response

- from flask_restful import Resource

- 

- from sqlalchemy.exc import IntegrityError

- 

- from ... import db

- from ...logic.coprs_logic import MockChrootsLogic, CoprChrootsLogic

- from ...exceptions import InsufficientRightsException, MalformedArgumentException

- 

- from ..exceptions import AccessForbidden, MalformedRequest, \

-     ObjectAlreadyExists, ObjectNotFoundError

- from ..common import rest_api_auth_required, render_copr_chroot, get_project_safe

- from ..schemas import CoprChrootSchema, CoprChrootCreateSchema

- from ..util import get_one_safe, mm_deserialize

- 

- 

- class ProjectChrootListR(Resource):

- 

-     @classmethod

-     def get(cls, project_id):

-         project = get_project_safe(project_id)

- 

-         return {

-             "chroots": [

-                 render_copr_chroot(chroot)

-                 for chroot in project.active_copr_chroots

-             ],

-             "_links": {

-                 "self": {"href": url_for(".projectchrootlistr", project_id=project_id)}

-             }

-         }

- 

-     @classmethod

-     @rest_api_auth_required

-     def post(cls, project_id):

-         project = get_project_safe(project_id)

- 

-         req = mm_deserialize(CoprChrootCreateSchema(),

-                              flask.request.data.decode("utf-8"))

-         name = req.pop("name")

- 

-         try:

-             mock_chroot = get_one_safe(MockChrootsLogic.get_from_name(name))

-         except (MalformedArgumentException, ObjectNotFoundError) as err:

-             raise MalformedRequest("Bad mock chroot name: {}. Error: {}".format(name, err))

- 

-         try:

-             CoprChrootsLogic.create_chroot(flask.g.user, project, mock_chroot, **req)

-             db.session.commit()

-         except IntegrityError as err:

-             # assuming conflict with existing chroot

-             db.session.rollback()

-             if get_one_safe(CoprChrootsLogic.get_by_name(project, name)) is not None:

-                 raise ObjectAlreadyExists("Copr {} already has chroot {} enabled"

-                                           .format(project.full_name, name))

- 

-         resp = make_response("", 201)

-         resp.headers["Location"] = url_for(".projectchrootr",

-                                            project_id=project.id, name=name)

-         return resp

- 

- 

- class ProjectChrootR(Resource):

- 

-     @staticmethod

-     def _get_chroot_safe(copr, name):

-         try:

-             chroot = get_one_safe(CoprChrootsLogic.get_by_name(copr, name))

-         except MalformedArgumentException as err:

-             raise MalformedRequest("Bad mock chroot name: {}".format(err))

-         return chroot

- 

-     def get(self, project_id, name):

-         project = get_project_safe(project_id)

-         chroot = self._get_chroot_safe(project, name)

- 

-         return render_copr_chroot(chroot)

- 

-     @classmethod

-     @rest_api_auth_required

-     def delete(cls, project_id, name):

-         project = get_project_safe(project_id)

-         chroot = CoprChrootsLogic.get_by_name_safe(project, name)

- 

-         if chroot:

-             try:

-                 CoprChrootsLogic.remove_copr_chroot(flask.g.user, chroot)

-             except InsufficientRightsException as err:

-                 raise AccessForbidden("Failed to remove copr chroot: {}".format(err))

- 

-             db.session.commit()

- 

-         return "", 204

- 

-     @rest_api_auth_required

-     def put(self, project_id, name):

-         project = get_project_safe(project_id)

-         chroot = self._get_chroot_safe(project, name)

- 

-         chroot_data = mm_deserialize(CoprChrootSchema(), flask.request.data.decode("utf-8"))

-         try:

-             updated_chroot = CoprChrootsLogic.update_chroot(

-                 user=flask.g.user,

-                 copr_chroot=chroot,

-                 **chroot_data

-             )

-         except InsufficientRightsException as err:

-             raise AccessForbidden("Failed to update copr chroot: {}".format(err))

- 

-         db.session.commit()

-         return render_copr_chroot(updated_chroot)

@@ -1,211 +0,0 @@ 

- # coding: utf-8

- 

- from collections.abc import Iterable

- from marshmallow import Schema as _Schema, fields, ValidationError, validate

- 

- 

- loads_kwargs = {}

- try:

-     from marshmallow.utils import EXCLUDE

-     loads_kwargs = {"unknown": EXCLUDE}

- except ImportError:

-     pass

- 

- 

- class Schema(_Schema):

-     # In marshmallow v3+ there's no data/errors attributes as it was

-     # in v2.  So we need to have compat wrapper.

- 

-     def wrap_function(self, name, *args, **kwargs):

-         super_object = super(Schema, self)

-         super_result = getattr(super_object, name)(*args, **kwargs)

-         if hasattr(super_result, 'data') and hasattr(super_result, 'errors'):

-             # old marshmallow

-             data = super_result.data

-             if super_result.errors:

-                 exception = ValidationError(super_result.errors)

-                 exception.valid_data = data

-                 raise exception

-             return data

-         else:

-             return super_result

- 

-     def dump(self, *args, **kwargs):

-         return self.wrap_function('dump', *args, **kwargs)

- 

-     def loads(self, *args, **kwargs):

-         kwargs.update(loads_kwargs)

-         return self.wrap_function('loads', *args, **kwargs)

- 

- def validate_any(fn_list):

-     """

-     :param fn_list: list of callable functions, each takes one param

-     :return: None if at least one validation function exists without exceptions

-     :raises ValidationError: otherwise

-     """

-     def func(value):

-         errors = []

-         for fn in fn_list:

-             try:

-                 fn(value)

-             except ValidationError as err:

-                 errors.append(str(err))

-             else:

-                 return

-         else:

-             errors.insert(0, u"At least one validator should accept given value:")

-             raise ValidationError(errors)

- 

-     return func

- 

- 

- class SpaceSeparatedList(fields.Field):

-     def _serialize(self, value, attr, obj, **kwargs):

-         if value is None:

-             return []

-         return value.split()

- 

-     def _deserialize(self, value, attr=None, data=None, **kwargs):

-         if value is None:

-             return ""

-         elif not isinstance(value, Iterable) or isinstance(value, str):

-             raise ValidationError("Value `{}` is not a list of strings"

-                                   .format(value))

-         else:

-             return " ".join(value)

- 

- 

- class BuiltPackages(fields.Field):

-     """ stored in db as a string:

-     "python3-marshmallow 2.0.0b5\npython-marshmallow 2.0.0b5"

-     we would represent them as

-     { name: "pkg", version: "pkg version" }

-     we implement only the serialization, since field is read-only

-     """

-     def _serialize(self, value, attr, obj, **kwargs):

-         if value is None:

-             return []

-         result = []

-         try:

-             for chunk in value.split("\n"):

-                 pkg, version = chunk.split()

-                 result.append({

-                     "name": pkg,

-                     "version": version

-                 })

-         except:

-             pass

- 

-         return result

- 

- 

- class AllowedMethodSchema(Schema):

-     method = fields.Str()

-     doc = fields.Str()

-     require_auth = fields.Bool()

-     params = fields.List(fields.Str())

- 

- 

- class MockChrootSchema(Schema):

-     class Meta:

-         fields = ("name", "os_release", "os_version", "arch", "is_active")

-         ordered = True

- 

- 

- class ProjectSchema(Schema):

-     id = fields.Int(dump_only=True)

-     name = fields.Str(dump_only=True)

- 

-     owner = fields.Str(attribute="owner_name", dump_only=True)

-     is_a_group_project = fields.Bool(dump_only=True)

-     group = fields.Str(attribute="group_name", dump_only=True)

- 

-     description = fields.Str(allow_none=True)

-     instructions = fields.Str(allow_none=True)

-     homepage = fields.Url(allow_none=True)

-     contact = fields.Str(validate=validate_any([

-         validate.URL(),

-         validate.Email(),

-         validate.OneOf(["", None]),

-     ]), allow_none=True)

- 

-     disable_createrepo = fields.Bool(allow_none=True)

-     build_enable_net = fields.Bool(allow_none=True)

-     last_modified = fields.DateTime(dump_only=True)

- 

-     repos = SpaceSeparatedList(allow_none=True)

- 

- 

- class ProjectCreateSchema(ProjectSchema):

-     name = fields.Str(

-         required=True,

-         validate=[

-             validate.Regexp(

-                 r"^[a-zA-Z][\w.-]*$",

-                 error="Name must contain only letters,"

-                       "digits, underscores, dashes and dots."

-                       "And starts with letter"),

-         ])

-     group = fields.Str(load_only=True, allow_none=True)

-     chroots = SpaceSeparatedList(load_only=True, default=list)

- 

- 

- # todo: rename to ProjectChrootSchema

- class CoprChrootSchema(Schema):

- 

-     buildroot_pkgs = SpaceSeparatedList()

-     name = fields.Str(dump_only=True)

- 

-     comps = fields.Str(allow_none=True)

-     comps_name = fields.Str(allow_none=True)

-     comps_len = fields.Int(dump_only=True)

- 

- 

- class CoprChrootCreateSchema(CoprChrootSchema):

-     name = fields.Str(required=True)

- 

- 

- class BuildTaskSchema(Schema):

-     # used only for presentation

-     state = fields.Str()

-     started_on = fields.Int(dump_only=True)

-     ended_on = fields.Int(dump_only=True)

-     git_hash = fields.Str(dump_only=True)

-     chroot_name = fields.Str(dump_only=True, attribute="name")

-     build_id = fields.Int(dump_only=True)

- 

-     result_dir_url = fields.Str(dump_only=True)

- 

- 

- class BuildSchema(Schema):

- 

-     id = fields.Int(dump_only=True)

-     state = fields.Str()

- 

-     submitter = fields.Str(dump_only=True, attribute="user_name")

- 

-     built_packages = BuiltPackages(dump_only=True)

-     package_version = fields.Str(dump_only=True, attribute="pkg_version")

-     package_name = fields.Str(dump_only=True)

- 

-     repos = SpaceSeparatedList(dump_only=True)

- 

-     submitted_on = fields.Int(dump_only=True)

- 

-     # timeout = fields.Int(dump_only=True)  # currently has no use

-     enable_net = fields.Bool(dump_only=True)

- 

-     source_type = fields.Str(dump_only=True, attribute="source_type_text")

-     source_metadata = fields.Raw(dump_only=True)

- 

- 

- class BuildCreateSchema(BuildSchema):

-     project_id = fields.Int(required=True)

-     chroots = fields.List(fields.Str())

-     enable_net = fields.Bool()

- 

-     state = fields.Str(dump_only=True)

- 

- 

- class BuildCreateFromUrlSchema(BuildCreateSchema):

-     srpm_url = fields.Url(required=True, validate=lambda u: u.startswith("http"))

@@ -1,100 +0,0 @@ 

- # coding: utf-8

- import json

- 

- import sqlalchemy.orm.exc

- 

- from flask_restful.reqparse import Argument, RequestParser

- 

- from marshmallow.exceptions import ValidationError

- from .exceptions import ObjectNotFoundError, MalformedRequest

- from .schemas import AllowedMethodSchema

- 

- 

- class AllowedMethod(object):

-     def __init__(self, method, doc, require_auth=True, params=None):

-         self.method = method

-         self.doc = doc

-         self.require_auth = require_auth

-         self.params = params or []

- 

- 

- def render_allowed_method(method, doc, require_auth=True, params=None):

-     method = AllowedMethod(method, doc, require_auth, params)

-     return mm_serialize_one(AllowedMethodSchema, method)

- 

- 

- def get_one_safe(query, msg=None, data=None):

-     """

-     :type query:  sqlalchemy.Query

-     :param str msg: message used in error when object not found

-     :param Any data: any serializable data to include into error

-     :raises ObjectNotFoundError: when query failed to return anything

-     """

-     try:

-         return query.one()

-     except sqlalchemy.orm.exc.NoResultFound:

-         raise ObjectNotFoundError(msg=msg, data=data)

- 

- 

- def json_loads_safe(raw, data_on_error=None):

-     try:

-         return json.loads(raw)

-     except ValueError:

-         raise MalformedRequest(data_on_error or

-                                "Failed to deserialize json string")

- 

- 

- def mm_deserialize(schema, json_string):

-     try:

-         return schema.loads(json_string)

-     except (ValueError, ValidationError) as err:

-         raise MalformedRequest(

-             msg="Failed to parse request: {}".format(err),

-             data={"request_string": json_string}

-         )

- 

- def mm_serialize_one(schema, obj):

-     try:

-         return schema().dump(obj)

-     except ValidationError as err:

-         # This isn't correct behavior, but we used to ignore ValidationError

-         # before (e.g rhbz#1758559) and there's no desire to fix APIv2 when

-         # there's APIv3.

-         return err.valid_data

- 

- 

- class MyArg(Argument):

-     def handle_validation_error(self, error, bundle_errors):

-         # dirty monkey patching, better to switch to webargs

-         # bundle errors are ignored

-         data = {u"error": str(error)}

-         if self.help:

-             data["help"] = self.help

-         raise MalformedRequest(

-             "Failed to validate query parameter: {}".format(self.name),

-             data=data

-         )

- 

- 

- def get_request_parser():

-     return RequestParser(argument_class=MyArg)

- 

- 

- def arg_bool(value):

-     """

-     :param str value: value to convert

-     :rtype: bool

-     :raises ValueError:

-     """

-     true_values = ["true", "yes", "1"]

-     false_values = ["false", "no", "0"]

-     value = str(value).strip().lower()

- 

-     if value in true_values:

-         return True

-     elif value in false_values:

-         return False

- 

-     raise ValueError("Value `{}` doesn't belong to either true of false sets. "

-                      "Allowed values: {} and {}"

-                      .format(value, true_values, false_values))

@@ -448,7 +448,7 @@ 

    {% if build.group_name %}

      {{- url_for('coprs_ns.copr_build', group_name=(build.copr.group.name if build.copr is defined else build.group_name), coprname=build.copr_name, build_id=build.id) -}}

    {% else %}

-     {{- url_for("coprs_ns.copr_build", username=(build.copr.user.name if build.copr is defined else build.user_name), coprname = build.copr_name, build_id = build.id) -}}

+     {{- url_for("coprs_ns.copr_build", username=(build.copr.user.name if build.copr is defined else build.user.name), coprname = build.copr_name, build_id = build.id) -}}

    {% endif %}

  {% endmacro %}

  

@@ -792,42 +792,6 @@ 

          with self.setup_user_session(self.u1):

              yield

  

-     def request_rest_api_with_auth(self, url,

-                                    login=None, token=None,

-                                    content=None, method="GET",

-                                    headers=None, data=None,

-                                    content_type="application/json"):

-         """

-         :rtype: flask.wrappers.Response

-         Requires f_users_api fixture

-         """

-         if login is None:

-             login = self.user_api_creds["user1"]["login"]

-         if token is None:

-             token = self.user_api_creds["user1"]["token"]

- 

-         req_headers = {

-             "Authorization": self._get_auth_string(login, token),

-         }

-         if headers:

-             req_headers.update(headers)

- 

-         kwargs = dict(

-             method=method,

-             content_type=content_type,

-             headers=req_headers,

-             buffered=True,

-         )

-         if content is not None and data is not None:

-             raise RuntimeError("Don't specify content and data together")

- 

-         if content:

-             kwargs["data"] = json.dumps(content)

-         if data:

-             kwargs["data"] = data

- 

-         return self.tc.open(url, **kwargs)

- 

      def _get_auth_string(self, login, token):

          userstring = "{}:{}".format(login, token).encode("utf-8")

          base64string_user = base64.b64encode(userstring)

@@ -1,553 +0,0 @@ 

- # coding: utf-8

- from io import BytesIO

- import json

- import math

- import random

- 

- from copr_common.enums import StatusEnum

- from coprs.logic.actions_logic import ActionsLogic

- from coprs.logic.builds_logic import BuildsLogic

- from tests.coprs_test_case import CoprsTestCase

- 

- 

- class TestBuildResource(CoprsTestCase):

- 

-     @staticmethod

-     def extract_build_ids(response_object):

-         return set([

-             b_dict["build"]["id"]

-             for b_dict in response_object["builds"]

-         ])

- 

-     def test_build_collection_ok(self, f_users, f_coprs, f_builds, f_db,

-                            f_users_api, f_mock_chroots):

- 

-         href = "/api_2/builds"

-         self.db.session.commit()

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

- 

-         # not a pure test, but we test API here

-         builds = BuildsLogic.get_multiple().all()

-         expected_ids = set([b.id for b in builds])

- 

-         assert expected_ids == self.extract_build_ids(obj)

- 

-     def test_build_collection_ok_finished(

-             self, f_users, f_coprs, f_mock_chroots,  f_builds, f_db):

- 

-         self.db.session.commit()

- 

-         href_a = "/api_2/builds?is_finished=True"

-         href_b = "/api_2/builds?is_finished=False"

- 

-         r_a = self.tc.get(href_a)

-         r_b = self.tc.get(href_b)

- 

-         assert r_a.status_code == 200

-         assert r_b.status_code == 200

-         obj_a = json.loads(r_a.data.decode("utf-8"))

-         obj_b = json.loads(r_b.data.decode("utf-8"))

- 

-         builds = BuildsLogic.get_multiple().all()

-         expected_ids_a = set([b.id for b in builds if b.max_ended_on is not None])

-         expected_ids_b = set([b.id for b in builds if b.max_ended_on is None])

- 

-         assert expected_ids_a == self.extract_build_ids(obj_a)

-         assert expected_ids_b == self.extract_build_ids(obj_b)

- 

-     def test_build_collection_by_user(self, f_users, f_coprs, f_builds, f_db,

-                            f_users_api, f_mock_chroots):

- 

-         names_list = [user.username for user in self.basic_user_list]

-         for user_name in names_list:

-             href = "/api_2/builds?owner={}".format(user_name)

-             self.db.session.commit()

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

- 

-             # not a pure test, but we test API here

-             builds = [

-                 b for b in BuildsLogic.get_multiple().all()

-                 if b.copr.user.username == user_name

-             ]

-             expected_ids = set([b.id for b in builds])

-             assert expected_ids == self.extract_build_ids(obj)

- 

-     def test_build_collection_by_project_id(

-             self, f_users, f_mock_chroots, f_coprs,  f_builds, f_db):

- 

-         project_id_list = [copr.id for copr in self.basic_coprs_list]

-         for id_ in project_id_list:

-             href = "/api_2/builds?project_id={}".format(id_)

-             self.db.session.commit()

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

- 

-             # not a pure test, but we test API here

-             builds = [

-                 b for b in BuildsLogic.get_multiple().all()

-                 if b.copr.id == id_

-             ]

-             expected_ids = set([b.id for b in builds])

-             assert expected_ids == self.extract_build_ids(obj)

- 

-     def test_build_collection_limit_offset(

-             self, f_users, f_mock_chroots, f_coprs, f_builds, f_db):

- 

-         self.db.session.commit()

-         builds = BuildsLogic.get_multiple().all()

-         total = len(builds)

- 

-         # test limit

-         for lim in range(1, total + 1):

-             href = "/api_2/builds?limit={}".format(lim)

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

-             builds = obj["builds"]

-             assert len(builds) == lim

- 

-             if lim > 2:

-                 delta = int(math.floor(lim / 2))

-                 href1 = "/api_2/builds?limit={}".format(delta)

-                 href2 = "/api_2/builds?limit={0}&offset={0}".format(delta)

- 

-                 r1 = self.tc.get(href1)

-                 r2 = self.tc.get(href2)

- 

-                 assert r1.status_code == 200

-                 assert r2.status_code == 200

- 

-                 obj1 = json.loads(r1.data.decode("utf-8"))

-                 obj2 = json.loads(r2.data.decode("utf-8"))

- 

-                 assert builds[:delta] == obj1["builds"]

-                 assert builds[delta:2 * delta] == obj2["builds"]

- 

-         # for coverage

-         href = "/api_2/builds?limit={}".format(1000000)

-         r = self.tc.get(href)

-         assert r.status_code == 200

- 

-     def test_build_post_bad_content_type(

-             self, f_users, f_coprs, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         self.db.session.commit()

-         metadata = {

-             "project_id": 1,

-             "srpm_url": "http://example.com/mypkg.src.rpm",

-             "chroots": chroot_name_list

-         }

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content_type="plain/test"

-         )

-         assert r0.status_code == 400

- 

-     def test_build_post_json_bad_url(

-             self, f_users, f_coprs, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         for url in [None, "", "dsafasdga", "gopher://mp.src.rpm"]:

-             metadata = {

-                 "project_id": 1,

-                 "srpm_url": url,

-                 "chroots": chroot_name_list

-             }

-             self.db.session.commit()

-             r0 = self.request_rest_api_with_auth(

-                 "/api_2/builds",

-                 method="post",

-                 content=metadata

-             )

-             assert r0.status_code == 400

- 

-     def test_build_post_json(

-             self, f_users, f_coprs, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

- 

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         metadata = {

-             "project_id": 1,

-             "srpm_url": "http://example.com/mypkg.src.rpm",

-             "chroots": chroot_name_list

-         }

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content=metadata

-         )

-         assert r0.status_code == 201

-         r1 = self.tc.get(r0.headers["Location"])

-         assert r1.status_code == 200

-         build_obj = json.loads(r1.data.decode("utf-8"))

-         build_dict = build_obj["build"]

-         assert build_dict["source_metadata"]["url"] == \

-             metadata["srpm_url"]

-         assert build_dict["source_type"] == "link"

- 

-     def test_build_post_json_on_wrong_user(

-             self, f_users, f_coprs, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

- 

-         login = self.u2.api_login

-         token = self.u2.api_token

- 

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         metadata = {

-             "project_id": 1,

-             "srpm_url": "http://example.com/mypkg.src.rpm",

-             "chroots": chroot_name_list

-         }

-         self.u1.admin = False

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             login=login, token=token,

-             content=metadata,

-         )

-         assert r0.status_code == 403

- 

-     def test_build_post_json_on_project_during_action(

-             self, f_users, f_coprs, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

- 

-         ActionsLogic.send_delete_copr(self.c1)

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         metadata = {

-             "project_id": 1,

-             "srpm_url": "http://example.com/mypkg.src.rpm",

-             "chroots": chroot_name_list

-         }

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content=metadata,

-         )

-         assert r0.status_code == 400

- 

-     def test_build_post_multipart_wrong_user(

-             self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         metadata = {

-             "project_id": 1,

-             "enable_net": True,

-             "chroots": chroot_name_list

-         }

-         data = {

-             "metadata": json.dumps(metadata),

-             "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')

-         }

-         login = self.u2.api_login

-         token = self.u2.api_token

-         self.u1.admin = False

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             login=login, token=token,

-             content_type="multipart/form-data",

-             data=data

-         )

-         assert r0.status_code == 403

- 

-     def test_build_post_multipart_on_project_during_action(

-             self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

- 

-         ActionsLogic.send_delete_copr(self.c1)

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         metadata = {

-             "project_id": 1,

-             "enable_net": True,

-             "chroots": chroot_name_list

-         }

-         data = {

-             "metadata": json.dumps(metadata),

-             "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')

-         }

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content_type="multipart/form-data",

-             data=data

-         )

-         assert r0.status_code == 400

- 

-     def test_build_post_multipart(

-             self, f_users, f_coprs, f_builds, f_db, f_mock_chroots,

-             f_mock_chroots_many, f_build_many_chroots,

-             f_users_api):

-         chroot_name_list = [c.name for c in self.c1.active_chroots]

-         metadata = {

-             "project_id": 1,

-             "enable_net": True,

-             "chroots": chroot_name_list

-         }

-         data = {

-             "metadata": json.dumps(metadata),

-             "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')

-         }

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content_type="multipart/form-data",

-             data=data

-         )

-         assert r0.status_code == 201

-         r1 = self.tc.get(r0.headers["Location"])

-         assert r1.status_code == 200

-         build_obj = json.loads(r1.data.decode("utf-8"))

- 

-         assert build_obj["build"]["source_type"] == "upload"

- 

-         tasks_href = build_obj["_links"]["build_tasks"]["href"]

-         r2 = self.tc.get(tasks_href)

-         build_chroots_obj = json.loads(r2.data.decode("utf-8"))

-         build_chroots_names = set([bc["build_task"]["chroot_name"] for bc in

-                                    build_chroots_obj["build_tasks"]])

-         assert set(chroot_name_list) == build_chroots_names

-         assert len(chroot_name_list) == len(build_chroots_obj["build_tasks"])

- 

-     def test_build_post_multipart_missing_file(

-             self,f_users, f_coprs, f_builds, f_db,

-             f_users_api, f_mock_chroots):

- 

-         metadata = {

-             "enable_net": True

-         }

-         data = {

-             "metadata": json.dumps(metadata),

-         }

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content_type="multipart/form-data",

-             data=data

-         )

-         assert r0.status_code == 400

- 

-     def test_build_post_multipart_missing_metadata(

-             self,f_users, f_coprs, f_builds, f_db,

-             f_users_api, f_mock_chroots):

-         data = {

-             "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')

-         }

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/builds",

-             method="post",

-             content_type="multipart/form-data",

-             data=data

-         )

-         assert r0.status_code == 400

- 

-     def test_build_get_one(self, f_users, f_coprs, f_builds, f_db,

-                      f_users_api, f_mock_chroots):

- 

-         build_id_list = [b.id for b in self.basic_builds]

-         self.db.session.commit()

- 

-         for b_id in build_id_list:

-             href = "/api_2/builds/{}".format(b_id)

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

-             assert obj["build"]["id"] == b_id

-             assert obj["_links"]["self"]["href"] == href

- 

-     def test_build_get_one_with_tasks(self, f_users, f_coprs, f_builds, f_db,

-                      f_users_api, f_mock_chroots):

- 

-         build_id_list = [b.id for b in self.basic_builds]

-         self.db.session.commit()

- 

-         for b_id in build_id_list:

-             href = "/api_2/builds/{}?show_build_tasks=True".format(b_id)

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

-             assert obj["build"]["id"] == b_id

-             assert obj["_links"]["self"]["href"] == href

-             assert "build_tasks" in obj

- 

-     def test_build_get_one_not_found(self, f_users, f_coprs, f_builds, f_db,

-                      f_users_api, f_mock_chroots):

-         build_id_list = [b.id for b in self.basic_builds]

-         max_id = max(build_id_list) + 1

-         self.db.session.commit()

- 

-         for _ in range(10):

-             fake_id = random.randint(max_id, max_id * 10)

-             href = "/api_2/builds/{}".format(fake_id)

- 

-             r = self.tc.get(href)

-             assert r.status_code == 404

- 

-     def test_build_delete_ok(self, f_users, f_coprs,

-                              f_mock_chroots, f_builds,f_users_api, ):

- 

-         self.db.session.commit()

-         self.b1.copr.appstream = True

-         b_id = self.b1.id

-         href = "/api_2/builds/{}".format(b_id)

-         r = self.request_rest_api_with_auth(

-             href,

-             method="delete",

-         )

-         assert r.status_code == 204

- 

-         r2 = self.tc.get(href)

-         assert r2.status_code == 404

- 

-     def test_build_delete_wrong_user(self, f_users, f_coprs,

-                              f_mock_chroots, f_builds,f_users_api, ):

- 

-         login = self.u2.api_login

-         token = self.u2.api_token

-         self.db.session.commit()

-         b_id = self.b1.id

-         href = "/api_2/builds/{}".format(b_id)

-         r = self.request_rest_api_with_auth(

-             href,

-             method="delete",

-             login=login, token=token,

-         )

-         assert r.status_code == 403

- 

-     def test_build_delete_in_progress(self, f_users, f_coprs,

-                              f_mock_chroots, f_builds,f_users_api, ):

- 

-         login = self.u2.api_login

-         token = self.u2.api_token

-         self.db.session.commit()

-         b_id = self.b3.id

-         href = "/api_2/builds/{}".format(b_id)

-         r = self.request_rest_api_with_auth(

-             href,

-             method="delete",

-             login=login, token=token,

-         )

-         assert r.status_code == 400

- 

-     def test_build_put_wrong_user(

-             self, f_users, f_coprs,

-             f_mock_chroots, f_builds,f_users_api, ):

- 

-         login = self.u2.api_login

-         token = self.u2.api_token

- 

-         for bc in self.b1_bc:

-             bc.status = StatusEnum("pending")

-             bc.ended_on = None

- 

-         self.u1.admin = False

-         

-         self.db.session.add_all(self.b1_bc)

-         self.db.session.add(self.b1)

- 

-         self.db.session.commit()

- 

-         href = "/api_2/builds/{}".format(self.b1.id)

-         build_dict = {

-             "state": "canceled"

-         }

-         r = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             login=login, token=token,

-             content=build_dict

-         )

-         assert r.status_code == 403

- 

-     def test_build_put_not_found(

-             self, f_users, f_coprs,

-             f_mock_chroots, f_builds,f_users_api, ):

- 

-         self.db.session.commit()

-         href = "/api_2/builds/{}".format(10005000)

-         build_dict = {

-             "state": "canceled"

-         }

-         r = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             content=build_dict

-         )

-         assert r.status_code == 404

- 

-     def test_build_put_cancel(

-             self, f_users, f_coprs,

-             f_mock_chroots, f_builds,f_users_api, ):

- 

-         for bc in self.b1_bc:

-             bc.status = StatusEnum("pending")

-             bc.ended_on = None

- 

-         self.db.session.add_all(self.b1_bc)

-         self.db.session.add(self.b1)

- 

-         self.db.session.commit()

- 

-         href = "/api_2/builds/{}".format(self.b1.id)

-         build_dict = {

-             "state": "canceled"

-         }

-         r = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             content=build_dict

-         )

-         assert r.status_code == 201

- 

-         r2 = self.tc.get(r.headers["Location"])

-         assert r2.status_code == 200

-         obj = json.loads(r2.data.decode("utf-8"))

-         assert obj["build"]["state"] == "canceled"

- 

-     def test_build_put_cancel_wrong_state(

-             self, f_users, f_coprs,

-             f_mock_chroots, f_builds,f_users_api, ):

- 

-         old_state = self.b1.state

-         self.db.session.add_all(self.b1_bc)

-         self.db.session.add(self.b1)

- 

-         self.db.session.commit()

- 

-         href = "/api_2/builds/{}".format(self.b1.id)

-         build_dict = {

-             "state": "canceled"

-         }

-         r = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             content=build_dict

-         )

-         assert r.status_code == 400

- 

-         r2 = self.tc.get(href)

-         assert r2.status_code == 200

-         obj = json.loads(r2.data.decode("utf-8"))

-         assert obj["build"]["state"] == old_state

- 

@@ -1,169 +0,0 @@ 

- # coding: utf-8

- import copy

- 

- import json

- from urllib.parse import urlparse, parse_qs

- 

- from copr_common.enums import StatusEnum

- 

- from tests.coprs_test_case import CoprsTestCase

- 

- 

- class TestBuildTaskResource(CoprsTestCase):

- 

-     def test_collection_ok(self, f_users, f_coprs, f_mock_chroots, f_builds, f_db,

-                            f_users_api):

- 

-         href = "/api_2/build_tasks?build_id=1"

-         bc_list = copy.deepcopy(self.b1_bc)

- 

-         self.db.session.commit()

- 

-         r0 = self.tc.get(href)

-         assert r0.status_code == 200

-         obj = json.loads(r0.data.decode("utf-8"))

-         assert len(obj["build_tasks"]) == len(bc_list)

- 

-     def test_collection_ok_default_limit(self, f_users, f_coprs, f_mock_chroots, f_builds, f_db,

-                            f_users_api):

- 

-         self.db.session.commit()

-         href_tpl = "/api_2/build_tasks?limit={}"

-         expected = href_tpl.format(100)

- 

-         for val in [-1, 0, 100, 105, 1000]:

-             href = href_tpl.format(val)

- 

-             r0 = self.tc.get(href)

-             assert r0.status_code == 200

-             obj = json.loads(r0.data.decode("utf-8"))

-             assert obj["_links"]["self"]["href"] == expected

- 

-     def test_collection_ok_by_state(

-             self, f_users, f_coprs,

-             f_mock_chroots,

-             f_mock_chroots_many,

-             f_build_many_chroots,

-             f_db,

-             f_users_api):

- 

-         self.db.session.commit()

-         for status in StatusEnum.vals.values():

-             expected_chroots = set([

-                 name

-                 for name, chroot_status in

-                 self.status_by_chroot.items()

-                 if chroot_status == status

-             ])

- 

-             href = "/api_2/build_tasks?state={}&limit=50".format(StatusEnum(status))

- 

-             r0 = self.tc.get(href)

-             assert r0.status_code == 200

-             obj = json.loads(r0.data.decode("utf-8"))

-             assert len(obj["build_tasks"]) == len(expected_chroots)

-             assert set(bt["build_task"]["chroot_name"]

-                        for bt in obj["build_tasks"]) == expected_chroots

- 

-             assert parse_qs(urlparse(obj["_links"]["self"]["href"]).query) \

-                 == parse_qs(urlparse(href).query)

- 

-     def test_collection_ok_by_project(

-             self, f_users, f_coprs, f_mock_chroots, f_builds,

-            f_users_api, f_db):

- 

-         href = "/api_2/build_tasks?project_id=1&limit=50"

-         bc_list = copy.deepcopy(self.b1_bc)

-         bc_list.extend(copy.deepcopy(self.b2_bc))

- 

-         self.db.session.commit()

- 

-         r0 = self.tc.get(href)

-         assert r0.status_code == 200

-         obj = json.loads(r0.data.decode("utf-8"))

-         assert len(obj["build_tasks"]) == len(bc_list)

-         assert parse_qs(urlparse(obj["_links"]["self"]["href"]).query) \

-             == parse_qs(urlparse(href).query)

- 

-     def test_collection_ok_by_user(

-             self, f_users, f_coprs, f_mock_chroots, f_builds,

-            f_users_api, f_db):

- 

-         href = "/api_2/build_tasks?owner={}&limit=50".format(self.u2.username)

-         bc_list_len = sum(

-             len(b.build_chroots)

-             for b in self.basic_builds

-             if b.copr.user == self.u2

-         )

- 

-         self.db.session.commit()

- 

-         r0 = self.tc.get(href)

-         assert r0.status_code == 200

-         obj = json.loads(r0.data.decode("utf-8"))

-         assert len(obj["build_tasks"]) == bc_list_len

-         assert parse_qs(urlparse(obj["_links"]["self"]["href"]).query) \

-             == parse_qs(urlparse(href).query)

- 

-     def test_post_not_allowed(self, f_users, f_coprs, f_mock_chroots, f_builds, f_db,

-                            f_users_api):

-         self.db.session.commit()

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/build_tasks",

-             method="post",

-             content={},

-         )

-         assert r0.status_code == 405

- 

-     def test_get_one(self, f_users, f_coprs, f_mock_chroots, f_builds, f_db,

-                            f_users_api):

-         expected_fields = [

-             ("state", None),

-             ("git_hash", None),

-             ("started_on", None),

-             ("ended_on", None),

-             ("name", "chroot_name"),

-         ]

-         bc = self.b1_bc[0]

-         expected_dict = {

-             f: getattr(bc, f)

-             for f, _ in expected_fields

-         }

-         href = "/api_2/build_tasks/1/{}".format(bc.name)

-         self.db.session.commit()

- 

-         r0 = self.tc.get(href)

-         assert r0.status_code == 200

-         obj = json.loads(r0.data.decode("utf-8"))

-         for k, res_k in expected_fields:

-             if res_k is None:

-                 res_k = k

-             assert obj["build_task"][res_k] == expected_dict[k]

- 

-     def test_get_one_bad_name(self, f_users, f_coprs, f_mock_chroots, f_builds, f_db,

-                            f_users_api):

- 

-         href = "/api_2/build_tasks/1/surely_bad_chroot_name"

-         self.db.session.commit()

- 

-         r0 = self.tc.get(href)

-         assert r0.status_code == 400

- 

-     def get_test_not_found(

-             self, f_users, f_coprs, f_mock_chroots, f_builds, f_db, f_users_api):

- 

-         bc = self.b1_bc[0]

-         href = "/api_2/builds/48545656/chroots/{}".format(bc.name)

-         href2 = "/api_2/builds/1/chroots/{}".format(bc.name)

-         self.db.session.commit()

- 

-         r0 = self.tc.get(href)

-         assert r0.status_code == 404

- 

-         r1 = self.tc.get(href2)

-         assert r1.status_code == 404

- 

- 

-     # def test_put_cancel_build_chroot

- 

- 

@@ -1,46 +0,0 @@ 

- # coding: utf-8

- 

- import json

- 

- from tests.coprs_test_case import CoprsTestCase

- 

- 

- class TestMockChrootResource(CoprsTestCase):

- 

-     def test_collection(self, f_mock_chroots, f_db):

-         href = "/api_2/mock_chroots"

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert obj["_links"]["self"]["href"] == href

-         assert len(obj["chroots"]) == len(self.mc_basic_list)

- 

-     def test_collection_only_active(self, f_mock_chroots, f_db):

-         expected_len = len(self.mc_basic_list) - 1

-         self.mc4.is_active = False

-         self.db.session.add(self.mc4)

-         self.db.session.commit()

- 

-         href = "/api_2/mock_chroots?active_only=True"

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert obj["_links"]["self"]["href"] == href

-         assert len(obj["chroots"]) == expected_len

- 

-     def test_post_not_allowed(self, f_mock_chroots, f_db, f_users, f_users_api):

-         r0 = self.request_rest_api_with_auth(

-             "/api_2/mock_chroots",

-             method="post",

-             content={},

-         )

-         assert r0.status_code == 405

- 

-     def test_get_one(self, f_mock_chroots, f_db):

-         chroot_name = self.mc1.name

-         href = "/api_2/mock_chroots/{}".format(chroot_name)

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert obj["_links"]["self"]["href"] == href

-         assert obj["chroot"]["name"] == chroot_name

@@ -1,307 +0,0 @@ 

- # coding: utf-8

- 

- import json

- 

- from tests.coprs_test_case import CoprsTestCase

- 

- 

- class TestProjectChrootResource(CoprsTestCase):

-     def test_remove_chroot(self, f_users, f_coprs, f_db, f_users_api,

-                            f_mock_chroots_many):

- 

-         init_len = len([mc for mc in self.mc_list if mc.is_active])

-         chroot_name = self.mc_list[0].name

-         self.db.session.commit()

- 

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == init_len

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="delete")

- 

-         assert r1.status_code == 204

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         assert r2.status_code == 200

-         assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == init_len - 1

- 

-         # test idempotency

-         r3 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="delete")

- 

-         assert r3.status_code == 204

-         r4 = self.tc.get("/api_2/projects/1/chroots")

-         assert r4.status_code == 200

-         assert len(json.loads(r4.data.decode("utf-8"))["chroots"]) == init_len - 1

- 

-     def test_remove_chroot_other_user(

-             self, f_users, f_coprs,f_db, f_users_api,

-             f_mock_chroots_many, f_build_few_chroots):

- 

-         init_len = len([mc for mc in self.mc_list if mc.is_active])

-         chroot_name = self.mc_list[0].name

-         login = self.u2.api_login

-         token = self.u2.api_token

-         self.db.session.commit()

- 

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == init_len

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             login=login, token=token,

-             method="delete")

- 

-         assert r1.status_code == 403

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         assert r2.status_code == 200

-         assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == init_len

- 

-     def test_put_correct(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         chroot_name = self.mc1.name

-         self.db.session.commit()

- 

-         data = {

-             "buildroot_pkgs": ["foo", "bar"],

-             "comps": "<comps><group></group></comps>",

-             "comps_name": "test.xml",

-         }

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="put",

-             content=data

-         )

-         assert r1.status_code == 200

-         r2 = self.tc.get("/api_2/projects/1/chroots/{}".format(chroot_name))

-         assert r2.status_code == 200

- 

-         def assert_content(response_data):

-             for k, v in data.items():

-                 assert response_data.get(k) == v

- 

-         assert_content(json.loads(r2.data.decode("utf-8"))["chroot"])

- 

-         # test idempotency

-         self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="put",

-             content=data

-         )

-         r3 = self.tc.get("/api_2/projects/1/chroots/{}".format(chroot_name))

-         assert r3.status_code == 200

-         assert_content(json.loads(r3.data.decode("utf-8"))["chroot"])

- 

-         # test put with excessive name field

-         data["name"] = chroot_name

-         r4 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="put",

-             content=data

-         )

-         assert r4.status_code == 200

- 

-     def test_put_erasing(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         chroot_name = self.mc1.name

-         self.db.session.commit()

- 

-         data = {

-             "buildroot_pkgs": [],

-             "comps": None,

-             "comps_name": None,

-         }

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="put",

-             content=data

-         )

-         assert r1.status_code == 200

-         r2 = self.tc.get("/api_2/projects/1/chroots/{}".format(chroot_name))

-         assert r2.status_code == 200

- 

-         def assert_content(response_data):

-             for k, v in data.items():

-                 assert response_data.get(k) == v

- 

-         assert_content(json.loads(r2.data.decode("utf-8"))["chroot"])

- 

-     def test_put_wrong_name(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         chroot_name = "completely_fake_chroot"

-         self.db.session.commit()

- 

-         data = {

-             "buildroot_pkgs": [],

-             "comps": None,

-             "comps_name": None,

-         }

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             method="put",

-             content=data

-         )

-         assert r1.status_code == 400

- 

-     def test_put_wrong_user(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         data = {

-             "buildroot_pkgs": [],

-             "comps": None,

-             "comps_name": None,

-         }

- 

-         chroot_name = self.mc1.name

-         login = self.u2.api_login

-         token = self.u2.api_token

-         self.db.session.commit()

- 

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots/{}".format(chroot_name),

-             login=login, token=token,

-             content=data,

-             method="put")

- 

-         assert r1.status_code == 403

- 

-     def test_create_chroot(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         new_chroot_name = self.mc2.name

- 

-         base_data = {

-             "buildroot_pkgs": ["foo", "bar"],

-             "comps": "<comps><group></group></comps>",

-             "comps_name": "test.xml",

-         }

-         data = {

-             "name": new_chroot_name,

-         }

-         data.update(base_data)

-         self.db.session.commit()

- 

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots",

-             method="post",

-             content=data

-         )

-         assert r1.status_code == 201

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         assert r2.status_code == 200

-         assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == 2

- 

-         r3 = self.tc.get(r1.headers["Location"])

- 

-         def assert_content(response_data):

-             for k, v in base_data.items():

-                 assert response_data.get(k) == v

- 

-         assert_content(json.loads(r3.data.decode("utf-8"))["chroot"])

- 

-     def test_create_chroot_build_no_buildchroot(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         new_chroot_name = self.mc2.name

- 

-         base_data = {

-             "comps": "<comps><group></group></comps>",

-             "comps_name": "test.xml",

-         }

-         data = {

-             "name": new_chroot_name,

-         }

-         data.update(base_data)

-         self.db.session.commit()

- 

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1

- 

-     def test_create_chroot_missing_name(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         base_data = {

-             "buildroot_pkgs": ["foo", "bar"],

-             "comps": "<comps><group></group></comps>",

-             "comps_name": "test.xml",

-         }

-         self.db.session.commit()

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots",

-             method="post",

-             content=base_data

-         )

-         assert r1.status_code == 400

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         assert r2.status_code == 200

-         assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == 1

- 

-     def test_create_chroot_non_existing_name(

-             self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

- 

-         new_chroot_name = "completely-fake-chroot"

-         base_data = {

-             "buildroot_pkgs": ["foo", "bar"],

-             "comps": "<comps><group></group></comps>",

-             "comps_name": "test.xml",

-         }

-         data = {

-             "name": new_chroot_name,

-         }

-         data.update(base_data)

-         self.db.session.commit()

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots",

-             method="post",

-             content=data

-         )

-         assert r1.status_code == 400

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         assert r2.status_code == 200

-         assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == 1

- 

-     def test_create_conflict_existing(

-             self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):

-         new_chroot_name = self.mc1.name

-         base_data = {

-             "buildroot_pkgs": ["foo", "bar"],

-             "comps": "<comps><group></group></comps>",

-             "comps_name": "test.xml",

-         }

-         data = {

-             "name": new_chroot_name,

-         }

-         data.update(base_data)

-         self.db.session.commit()

-         r0 = self.tc.get("/api_2/projects/1/chroots")

-         assert r0.status_code == 200

-         assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1

- 

-         r1 = self.request_rest_api_with_auth(

-             "/api_2/projects/1/chroots",

-             method="post",

-             content=data

-         )

-         assert r1.status_code == 409

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         assert r2.status_code == 200

-         assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == 1

- 

@@ -1,473 +0,0 @@ 

- # coding: utf-8

- 

- import copy

- import json

- from flask_whooshee import Whooshee

- 

- from coprs.logic.builds_logic import BuildsLogic

- 

- from coprs.logic.actions_logic import ActionsLogic

- from coprs.logic.coprs_logic import CoprsLogic

- from coprs.models import Copr

- from coprs.whoosheers import CoprWhoosheer

- from coprs import app

- 

- from tests.coprs_test_case import CoprsTestCase

- 

- 

- class TestProjectResource(CoprsTestCase):

-     put_update_dict = {

-         "description": "foo bar",

-         "instructions": "cthulhu fhtagn",

-         "repos": [

-             "http://example.com/repo",

-             "copr://foo/bar"

-             "copr://g/foo/bar"

-         ],

-         "disable_createrepo": True,

-         "build_enable_net": False,

-         "homepage": "http://example.com/foobar",

-         "contact": "foo@example.com",

-     }

- 

-     def test_project_list_self(self):

-         href = "/api_2/projects"

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert obj["_links"]["self"]["href"] == href

- 

-     def test_project_list_all(self, f_users, f_mock_chroots, f_coprs, f_db):

-         expected_id_set = set(c.id for c in self.basic_coprs_list)

-         href = "/api_2/projects"

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert set(p["project"]["id"] for p in obj["projects"]) == \

-             expected_id_set

- 

-     def test_project_list_by_user(self, f_users, f_mock_chroots, f_coprs, f_db):

-         expected_id_set = set(

-             c.id for c in self.basic_coprs_list

-             if c.user == self.u1

-         )

-         href = "/api_2/projects?owner={}".format(self.u1.username)

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert set(p["project"]["id"] for p in obj["projects"]) == \

-             expected_id_set

- 

-     def test_project_list_by_name(self, f_users, f_mock_chroots, f_coprs, f_db):

-         expected_id_set = set(

-             c.id for c in self.basic_coprs_list

-             if c.name == self.c1.name

-         )

-         href = "/api_2/projects?name={}".format(self.c1.name)

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

-         assert set(p["project"]["id"] for p in obj["projects"]) == \

-             expected_id_set

- 

-     def test_project_list_limit_offset(self, f_users, f_mock_chroots, f_coprs, f_db):

-         # quite hardcoded test

-         s_1 = set(p.id for p in [self.c1, self.c2])

-         s_2 = set(p.id for p in [self.c2, self.c3])

-         s_3 = set(p.id for p in [self.c3])

- 

-         href_list = [

-             "/api_2/projects?limit=2",

-             "/api_2/projects?limit=2&offset=1",

-             "/api_2/projects?limit=2&offset=2"

-         ]

-         for href, expected in zip(href_list, [s_1, s_2, s_3]):

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

-             assert set(p["project"]["id"] for p in obj["projects"]) == \

-                 expected

- 

-     def test_project_list_search(self, f_users, f_mock_chroots, f_coprs, f_db):

-         self.prefix = u"prefix"

-         self.s_coprs = []

-         c1_username = self.c1.user.username

- 

-         index = Whooshee.get_or_create_index(app, CoprWhoosheer)

-         writer = index.writer()

- 

-         k1 = 3

-         k2 = 5

-         for x in range(k1):

-             self.s_coprs.append(Copr(name=self.prefix + str(x), user=self.u1))

- 

-         for x in range(k2):

-             self.s_coprs.append(Copr(name=self.prefix + str(x), user=self.u2))

- 

-         self.db.session.add_all(self.s_coprs)

-         self.db.session.commit()

- 

-         for copr in self.s_coprs:

-             CoprWhoosheer.insert_copr(writer, copr)

-         writer.commit(optimize=True)

- 

-         r0 = self.tc.get(u"/api_2/projects?search_query={}".format(self.prefix))

-         assert r0.status_code == 200

-         obj = json.loads(r0.data.decode("utf-8"))

-         assert len(obj["projects"]) == k1 + k2

-         for p in obj["projects"]:

-             assert self.prefix in p["project"]["name"]

- 

-         r1 = self.tc.get(u"/api_2/projects?search_query={}&owner={}"

-                          .format(self.prefix, c1_username))

-         assert r1.status_code == 200

-         obj = json.loads(r1.data.decode("utf-8"))

-         assert len(obj["projects"]) == k1

-         for p in obj["projects"]:

-             assert self.prefix in p["project"]["name"]

- 

-     def test_project_create_new(self, f_users, f_mock_chroots, f_users_api):

-         self.db.session.add_all([self.u1, self.mc1])

-         self.db.session.commit()

- 

-         chroot_name = self.mc1.name

-         body = {

-             "name": "test_copr",

-             "chroots": [

-                 chroot_name,

-             ],

-             "repos": ["copr://bar/zar", ]

-         }

- 

-         r = self.request_rest_api_with_auth(

-             "/api_2/projects",

-             content=body, method="post")

-         assert r.status_code == 201

-         assert r.headers["Location"].endswith("/api_2/projects/1")

- 

-         r2 = self.tc.get("/api_2/projects/1/chroots")

-         copr_chroots_dict = json.loads(r2.data.decode("utf-8"))

-         assert len(copr_chroots_dict["chroots"]) == 1

-         assert copr_chroots_dict["chroots"][0]["chroot"]["name"] == chroot_name

- 

-     def test_project_create_bad_json(self, f_users, f_mock_chroots, f_users_api, f_db):

-         r = self.request_rest_api_with_auth(

-             "/api_2/projects",

-             data="fdf{fsd",

-             method="post")

- 

-         assert r.status_code == 400

- 

-     def test_project_create_bad_values(

-             self, f_users, f_mock_chroots,

-             f_users_api, f_db):

- 

-         href = "/api_2/projects"

-         cases = []

- 

-         def add_case(field, value):

-             t = copy.deepcopy(self.put_update_dict)

- 

-             t["name"] = "foobar_fake"

-             t["chroots"] = [mc.name for mc in self.mc_basic_list]

- 

-             t[field] = value

-             cases.append(t)

- 

-         add_case("repos", "foobar")

-         add_case("repos", 1)

-         add_case("contact", "adsg")

-         add_case("contact", "1")

-         add_case("homepage", "sdg")

- 

-         add_case("name", None)

-         add_case("name", "")

-         add_case("name", "3abc")

-         add_case("chroots", "sdg")

-         add_case("chroots", "")

- 

-         for test_case in cases:

-             r0 = self.request_rest_api_with_auth(

-                 href,

-                 method="post",

-                 content=test_case

-             )

-             assert r0.status_code == 400

- 

-     def test_project_create_ok_values(

-             self, f_users, f_mock_chroots,

-             f_users_api, f_db):

- 

-         href = "/api_2/projects"

-         cases = []

- 

-         self.counter = 0

- 

-         def add_case(field, value):

-             t = copy.deepcopy(self.put_update_dict)

-             t["name"] = "foobar_{}".format(self.counter)

-             t["chroots"] = [mc.name for mc in self.mc_basic_list]

-             self.counter += 1

- 

-             t[field] = value

-             cases.append(t)

- 

-         add_case("repos", [])

-         add_case("repos", None)

-         add_case("contact", "")

-         add_case("contact", None)

-         add_case("contact", "foo@bar.com")

-         add_case("homepage", "http://foo.org/bar/xdeeg?sdfg")

- 

-         add_case("name", "asdas-asdf")

-         add_case("name", "a2222222")

-         add_case("chroots", [])

- 

-         for test_case in cases:

-             r0 = self.request_rest_api_with_auth(

-                 href,

-                 method="post",

-                 content=test_case

-             )

-             assert r0.status_code == 201

- 

-     def test_project_create_new_project_exists(

-             self, f_users, f_mock_chroots, f_coprs, f_users_api):

-         self.db.session.add_all([self.u1, self.mc1])

-         self.db.session.commit()

- 

-         chroot_name = self.mc1.name

-         body = {

-             "name": self.c1.name,

-             "chroots": [

-                 chroot_name,

-             ],

-             "repos": ["copr://bar/zar", ]

-         }

-         r = self.request_rest_api_with_auth(

-             "/api_2/projects",

-             content=body, method="post")

-         assert r.status_code == 409

- 

-     def test_project_get_one_not_found(self, f_users, f_mock_chroots, f_db):

-         r = self.tc.get("/api_2/projects/1")

-         assert r.status_code == 404

- 

-     def test_project_get_one(self, f_users, f_mock_chroots, f_coprs, f_db):

- 

-         p_id_list = [p.id for p in self.basic_coprs_list]

-         for p_id in p_id_list:

-             href = "/api_2/projects/{}".format(p_id)

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

- 

-             assert obj["project"]["id"] == p_id

-             assert obj["_links"]["self"]["href"] == href

- 

-     def test_project_get_one_with_validation_error(self, f_users,

-                                                    f_mock_chroots, f_coprs,

-                                                    f_db):

-         self.c1.homepage = ""

-         p_id = self.c1.id

-         self.db.session.add(self.c1)

-         self.db.session.commit()

- 

-         href = "/api_2/projects/{}".format(p_id)

-         r = self.tc.get(href)

-         assert r.status_code == 200

-         obj = json.loads(r.data.decode("utf-8"))

- 

-         assert obj["project"]["id"] == p_id

-         assert obj["_links"]["self"]["href"] == href

- 

-     def test_project_get_one_with_chroots(self, f_users, f_mock_chroots, f_coprs, f_db):

- 

-         p_id_list = [p.id for p in self.basic_coprs_list]

-         for p_id in p_id_list:

-             href = "/api_2/projects/{}?show_chroots=True".format(p_id)

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

- 

-             assert obj["project"]["id"] == p_id

-             assert obj["_links"]["self"]["href"] == href

-             project = CoprsLogic.get_by_id(p_id).one()

-             assert len(obj["project_chroots"]) == len(project.copr_chroots)

- 

-     def test_project_get_one_with_builds(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_builds, f_db):

- 

-         p_id_list = [p.id for p in self.basic_coprs_list]

-         for p_id in p_id_list:

-             href = "/api_2/projects/{}?show_builds=True".format(p_id)

-             r = self.tc.get(href)

-             assert r.status_code == 200

-             obj = json.loads(r.data.decode("utf-8"))

- 

-             assert obj["project"]["id"] == p_id

-             assert obj["_links"]["self"]["href"] == href

-             project = CoprsLogic.get_by_id(p_id).one()

-             builds = BuildsLogic.get_multiple_by_copr(project).all()

-             assert len(obj["project_builds"]) == len(builds)

- 

-     def test_project_delete_not_found(

-             self, f_users, f_mock_chroots,

-             f_users_api, f_db):

- 

-         href = "/api_2/projects/{}".format("1")

- 

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="delete"

-         )

-         assert r0.status_code == 404

- 

-     def test_project_delete_ok(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

- 

-         href = "/api_2/projects/{}".format(self.c1.id)

- 

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="delete"

-         )

-         assert r0.status_code == 204

-         assert self.tc.get(href).status_code == 404

- 

-     def test_project_delete_fail_unfinished_build(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_builds, f_users_api, f_db):

-         href = "/api_2/projects/{}".format(self.c1.id)

- 

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="delete"

-         )

-         assert r0.status_code == 400

- 

-     def test_project_delete_fail_unfinished_project_action(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

- 

-         ActionsLogic.send_delete_copr(self.c1)

-         self.db.session.commit()

-         href = "/api_2/projects/{}".format(self.c1.id)

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="delete"

-         )

-         assert r0.status_code == 400

- 

-     def test_project_delete_wrong_user(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

- 

-         login = self.u2.api_login

-         token = self.u2.api_token

- 

-         href = "/api_2/projects/{}".format(self.c1.id)

- 

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="delete",

-             login=login, token=token,

-         )

-         assert r0.status_code == 403

- 

-     def test_project_put_ok(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

- 

-         href = "/api_2/projects/{}".format(self.c1.id)

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             content=self.put_update_dict

-         )

-         assert r0.status_code == 204

- 

-         r1 = self.tc.get(href)

-         obj = json.loads(r1.data.decode("utf-8"))

-         updated_project = obj["project"]

-         for k, v in self.put_update_dict.items():

-             assert updated_project[k] == v

- 

-     def test_project_put_wrong_user(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

-         login = self.u2.api_login

-         token = self.u2.api_token

- 

-         href = "/api_2/projects/{}".format(self.c1.id)

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             content=self.put_update_dict,

-             login=login, token=token,

-         )

-         assert r0.status_code == 403

- 

-     def test_project_put_not_found(

-             self, f_users, f_mock_chroots, f_users_api, f_db):

- 

-         href = "/api_2/projects/1"

-         r0 = self.request_rest_api_with_auth(

-             href,

-             method="put",

-             content=self.put_update_dict,

-         )

-         assert r0.status_code == 404

- 

-     def test_project_put_bad_values(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

-         href = "/api_2/projects/{}".format(self.c1.id)

-         cases = []

- 

-         def add_case(field, value):

-             t = copy.deepcopy(self.put_update_dict)

-             t[field] = value

-             cases.append(t)

- 

-         add_case("repos", "foobar")

-         add_case("repos", 1)

-         add_case("contact", "adsg")

-         add_case("contact", "1")

-         add_case("homepage", "sdg")

- 

-         for test_case in cases:

-             r0 = self.request_rest_api_with_auth(

-                 href,

-                 method="put",

-                 content=test_case

-             )

-             assert r0.status_code == 400

- 

-     def test_project_put_ok_values(

-             self, f_users, f_mock_chroots,

-             f_coprs, f_users_api, f_db):

-         href = "/api_2/projects/{}".format(self.c1.id)

-         cases = []

- 

-         def add_case(field, value):

-             t = copy.deepcopy(self.put_update_dict)

-             t[field] = value

-             cases.append(t)

- 

-         add_case("repos", [])

-         add_case("repos", None)

-         add_case("contact", "")

-         add_case("contact", None)

-         add_case("contact", "foo@bar.com")

-         add_case("homepage", "http://foo.org/bar/xdeeg?sdfg")

- 

-         for test_case in cases:

-             r0 = self.request_rest_api_with_auth(

-                 href,

-                 method="put",

-                 content=test_case

-             )

-             assert r0.status_code == 204

@@ -0,0 +1,35 @@ 

+ import pytest

+ from tests.coprs_test_case import CoprsTestCase, TransactionDecorator

+ 

+ 

+ class TestApiv3ProjectChroots(CoprsTestCase):

+ 

+     @TransactionDecorator("u1")

+     @pytest.mark.usefixtures("f_users", "f_coprs", "f_users_api",

+                              "f_mock_chroots", "f_db")

+     def test_edit_project_chroot(self):

+         self.db.session.add(self.c1)

+ 

+         chroot = self.c1.copr_chroots[0]

+         route = "/api_3/project-chroot/edit/{0}/{1}".format(

+             self.c1.full_name, chroot.name)

+ 

+         assert chroot.isolation == "unchanged"

+         assert chroot.bootstrap is None

+         assert chroot.buildroot_pkgs is None

+         assert chroot.module_toggle is None

+ 

+         data = {

+             "isolation": "nspawn",

+             "bootstrap": "off",

+             "additional_packages": ["foo", "bar"],

+             "additional_modules": ["!aaa:devel"],

+             "delete_comps": True,

+         }

+         response = self.api3.post(route, data)

+         assert response.status_code == 200

+ 

+         assert chroot.isolation == "nspawn"

+         assert chroot.bootstrap == "off"

+         assert chroot.buildroot_pkgs == "foo bar"

+         assert chroot.module_toggle == "!aaa:devel"

@@ -592,49 +592,3 @@ 

          build = models.Build.query.get(1)

          assert build.source_state == "succeeded"

          assert not os.path.exists(storage)

- 

- 

-     @pytest.mark.usefixtures("f_users", "f_coprs", "f_mock_chroots", "f_builds", "f_db")

-     def test_build_results_filter(self):

-         """

-         Test that we can query `BuildChroot` instances based on their `results`.

-         """

-         defaults = {

-             "name": None,

-             "epoch": 0,

-             "version": "1.0",

-             "release": "1",

-             "arch": None,

-         }

- 

-         b1b2_chroots = self.b1.build_chroots + self.b2.build_chroots

-         for chroot in b1b2_chroots:

-             result  = defaults | {"name": "foo", "arch": "x86_64"}

-             results = {"packages": [result]}

-             BuildChrootResultsLogic.create_from_dict(chroot, results)

- 

-         for chroot in self.b3.build_chroots:

-             result = defaults | {"name": "bar", "arch": "noarch"}

-             results = {"packages": [result]}

-             BuildChrootResultsLogic.create_from_dict(chroot, results)

- 

-         for chroot in self.b4.build_chroots:

-             result1 = defaults | {"name": "foobar", "arch": "ppc64le"}

-             result2 = defaults | {"name": "qux", "arch": "ppc64le"}

-             results = {"packages": [result1, result2]}

-             BuildChrootResultsLogic.create_from_dict(chroot, results)

- 

-         # Filter results by name

-         result = BuildChrootsLogic.get_by_results(name="foo").all()

-         assert set(result) == set(b1b2_chroots)

- 

-         # Filter results by multiple attributes

-         result = BuildChrootsLogic.get_by_results(name="foo", arch="noarch").all()

-         assert result == []

- 

-         result = BuildChrootsLogic.get_by_results(name="foo", arch="x86_64").all()

-         assert set(result) == set(b1b2_chroots)

- 

-         # Filter results with multiple rows

-         result = BuildChrootsLogic.get_by_results(name="qux").all()

-         assert set(result) == set(self.b4.build_chroots)

@@ -1,177 +0,0 @@ 

- # Makefile for Sphinx documentation

- #

- 

- # You can set these variables from the command line.

- SPHINXOPTS    =

- SPHINXBUILD   = sphinx-build

- PAPER         =

- BUILDDIR      = build

- 

- # User-friendly check for sphinx-build

- ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)

- $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)

- endif

- 

- # Internal variables.

- PAPEROPT_a4     = -D latex_paper_size=a4

- PAPEROPT_letter = -D latex_paper_size=letter

- ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source

- # the i18n builder cannot share the environment and doctrees with the others

- I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source

- 

- .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext

- 

- help:

- 	@echo "Please use \`make <target>' where <target> is one of"

- 	@echo "  html       to make standalone HTML files"

- 	@echo "  dirhtml    to make HTML files named index.html in directories"

- 	@echo "  singlehtml to make a single large HTML file"

- 	@echo "  pickle     to make pickle files"

- 	@echo "  json       to make JSON files"

- 	@echo "  htmlhelp   to make HTML files and a HTML help project"

- 	@echo "  qthelp     to make HTML files and a qthelp project"

- 	@echo "  devhelp    to make HTML files and a Devhelp project"

- 	@echo "  epub       to make an epub"

- 	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"

- 	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"

- 	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"

- 	@echo "  text       to make text files"

- 	@echo "  man        to make manual pages"

- 	@echo "  texinfo    to make Texinfo files"

- 	@echo "  info       to make Texinfo files and run them through makeinfo"

- 	@echo "  gettext    to make PO message catalogs"

- 	@echo "  changes    to make an overview of all changed/added/deprecated items"

- 	@echo "  xml        to make Docutils-native XML files"

- 	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"

- 	@echo "  linkcheck  to check all external links for integrity"

- 	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"

- 

- clean:

- 	rm -rf $(BUILDDIR)/*

- 

- html:

- 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html

- 	@echo

- 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

- 

- dirhtml:

- 	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml

- 	@echo

- 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."

- 

- singlehtml:

- 	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml

- 	@echo

- 	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."

- 

- pickle:

- 	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle

- 	@echo

- 	@echo "Build finished; now you can process the pickle files."

- 

- json:

- 	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json

- 	@echo

- 	@echo "Build finished; now you can process the JSON files."

- 

- htmlhelp:

- 	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp

- 	@echo

- 	@echo "Build finished; now you can run HTML Help Workshop with the" \

- 	      ".hhp project file in $(BUILDDIR)/htmlhelp."

- 

- qthelp:

- 	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp

- 	@echo

- 	@echo "Build finished; now you can run "qcollectiongenerator" with the" \

- 	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"

- 	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CoprApi2.qhcp"

- 	@echo "To view the help file:"

- 	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CoprApi2.qhc"

- 

- devhelp:

- 	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp

- 	@echo

- 	@echo "Build finished."

- 	@echo "To view the help file:"

- 	@echo "# mkdir -p $$HOME/.local/share/devhelp/CoprApi2"

- 	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CoprApi2"

- 	@echo "# devhelp"

- 

- epub:

- 	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub

- 	@echo

- 	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."

- 

- latex:

- 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex

- 	@echo

- 	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."

- 	@echo "Run \`make' in that directory to run these through (pdf)latex" \

- 	      "(use \`make latexpdf' here to do that automatically)."

- 

- latexpdf:

- 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex

- 	@echo "Running LaTeX files through pdflatex..."

- 	$(MAKE) -C $(BUILDDIR)/latex all-pdf

- 	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

- 

- latexpdfja:

- 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex

- 	@echo "Running LaTeX files through platex and dvipdfmx..."

- 	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja

- 	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

- 

- text:

- 	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text

- 	@echo

- 	@echo "Build finished. The text files are in $(BUILDDIR)/text."

- 

- man:

- 	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man

- 	@echo

- 	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."

- 

- texinfo:

- 	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo

- 	@echo

- 	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."

- 	@echo "Run \`make' in that directory to run these through makeinfo" \

- 	      "(use \`make info' here to do that automatically)."

- 

- info:

- 	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo

- 	@echo "Running Texinfo files through makeinfo..."

- 	make -C $(BUILDDIR)/texinfo info

- 	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."

- 

- gettext:

- 	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale

- 	@echo

- 	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."

- 

- changes:

- 	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes

- 	@echo

- 	@echo "The overview file is in $(BUILDDIR)/changes."

- 

- linkcheck:

- 	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck

- 	@echo

- 	@echo "Link check complete; look for any errors in the above output " \

- 	      "or in $(BUILDDIR)/linkcheck/output.txt."

- 

- doctest:

- 	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest

- 	@echo "Testing of doctests in the sources finished, look at the " \

- 	      "results in $(BUILDDIR)/doctest/output.txt."

- 

- xml:

- 	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml

- 	@echo

- 	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."

- 

- pseudoxml:

- 	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml

- 	@echo

- 	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

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

- sphinxcontrib-httpdomain

@@ -1,455 +0,0 @@ 

- Build

- =====

- 

- Build resource allows to submit new builds and access current build progress.

- 

- In fact, build consists of a few tasks, one per chroot, and detailed information

- is available through resource :doc:`./build_task`.

- 

- 

- 

- Structure of the build entity

- -----------------------------

- 

- .. code-block:: javascript

- 

-     {

-         "enable_net": true,

-         "source_metadata": {

-             "tmp": "tmpUNPJWO",

-             "pkg": "python-marshmallow-2.0.0b5-1.fc22.src.rpm"

-         },

-         "submitted_on": 1440753750,

-         "repos": [],

-         "built_packages": [

-             {

-                 "version": "2.0.0b5",

-                 "name": "python3-marshmallow"

-             },

-             {

-                 "version": "2.0.0b5",

-                 "name": "python-marshmallow"

-             }

-         ],

-         "started_on": null,

-         "source_type": "srpm_upload",

-         "state": "succeeded",

-         "ended_on": 1440754058,

-         "package_version": "2.0.0b5-1.fc22",

-         "package_name": "python-marshmallow",

-         "id": 106882,

-         "submitter": "asamalik"

-     }

- 

- 

- Build fields

- ~~~~~~~~~~~~

- ==================  ==================== ===============

- Field               Type                 Description

- ==================  ==================== ===============

- id                  int                  unique build identifier

- state               string               current state of the build, value is aggregated from build tasks

- submitted_on        int(unixtime UTC)    time of the build submission

- started_on          int(unixtime UTC)    time when the first build task started, otherwise ``null``

- ended_on            int(unixtime UTC)    time when the last build task ended, otherwise ``null``

- source_type         string               method used for build creation

- source_metadata     json object          build source information

- package_version     string               version of the source package

- package_name        string               name of the source package

- enable_net          bool                 defines if network is available during the build

- repos               list of string       list of additional repositories enabled during the build

- built_packages      list of hash maps    list of the built packages, each hash map has two keys: ``name`` and ``version``

- submitter           string               name of the user who submitted the build

- ==================  ==================== ===============

- 

- .. note::

-     Only the ``state`` field is editable by the PUT method.

-     All other fields are read-only.

-     There is a different structure used for build creation, see details at `Submit new build`_.

- 

- 

- List builds

- -----------

- .. http:post:: /api_2/builds

- 

-     Returns a list of builds according to the given query parameters.

- 

-     :query str owner: select only builds from projects owned by this user

-     :query str project_id: select only projects owned by this project

-     :query int offset: offset number, default value is 0

-     :query int limit: limit number between 1 and 100, default value is 100

- 

-     :statuscode 200: no error

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         GET /api_2/builds?project_id=3985&limit=1 HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "_links": {

-             "self": {

-               "href": "/api_2/builds?project_id=3985&limit=1"

-             }

-           },

-           "builds": [

-             {

-               "_links": {

-                 "project": {

-                   "href": "/api_2/projects/3985"

-                 },

-                 "self": {

-                   "href": "/api_2/builds/106897"

-                 },

-                 "build_tasks": {

-                   "href": "/api_2/build_tasks?build_id=106897"

-                 }

-               },

-               "build": {

-                 "enable_net": true,

-                 "source_metadata": {

-                   "url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"

-                 },

-                 "package_name": "copr-ping",

-                 "submitted_on": 1441366834,

-                 "package_version": "1-1.fc20",

-                 "built_packages": [

-                   {

-                     "version": "1",

-                     "name": "copr-ping"

-                   }

-                 ],

-                 "started_on": null,

-                 "source_type": "srpm_link",

-                 "state": "succeeded",

-                 "ended_on": 1441366969,

-                 "id": 106897,

-                 "repos": [],

-                 "submitter": "asamalik"

-               }

-             }

-           ]

-         }

- 

- 

- Submit new build

- ----------------

- **REQUIRES AUTH**

- 

- There are more ways to submit new build. Copr services currently provides the following options for build submission:

- 

- From SRPM URL

- ~~~~~~~~~~~~~

-     .. code-block:: javascript

- 

-         {

-             "project_id": 3985,

-             "chroots": ["fedora-22-i386", "fedora-21-i386"],

-             "srpm_url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"

-         }

- 

- 

-     ==================  ==================== ===============

-     Field               Type                 Description

-     ==================  ==================== ===============

-     project_id          int                  identifier of the parent project

-     chroots             list of strings      which chroots should be used for build

-     srpm_url            string(URL)          URL to the publicly available source package

-     enable_net          bool                 allows to disable network access during the build, default: True

-     ==================  ==================== ===============

- 

- .. http:post:: /api_2/builds

- 

-     :reqheader Content-Type: MUST be a ``application/json``

- 

-     :resheader Location: contains URL to the submitted build

- 

-     :statuscode 201: build was successfully submitted

-     :statuscode 400: user data doesn't satisfy some requirements

-     :statuscode 403: authorization failed

- 

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         POST /api_2/builds HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

-         Content-Type: application/json

- 

-         {

-             "project_id": 3985,

-             "chroots": ["fedora-22-i386", "fedora-21-i386"],

-             "srpm_url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"

-         }

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 201 CREATED

-         Location: /api_2/builds/106897

- 

- Using SRPM file upload

- ~~~~~~~~~~~~~~~~~~~~~~

- To upload source package you MUST use ``multipart/form-data`` content type.

- An additional build information MUST be present in ``metadata`` part in JSON format. Source package

- MUST be uploaded as binary  ``srpm`` file.

- 

- 

-     ****

- 

-     .. code-block:: javascript

- 

-         {

-             "project_id": 3985,

-             "chroots": ["fedora-22-i386", "fedora-21-i386"],

-             "enable_net": false

-         }

- 

-     ==================  ==================== ===============

-     Field               Type                 Description

-     ==================  ==================== ===============

-     project_id          int                  identifier of the parent project

-     chroots             list of strings      which chroots should be used for build

-     enable_net          bool                 allows to disable network access during the build, default: True

-     ==================  ==================== ===============

- 

- 

- .. http:post:: /api_2/builds

- 

-     :reqheader Content-Type: MUST be a ``multipart/form-data``

-     :formparam metadata: JSON with the build info, MUST have a content type ``application/json``

-     :formparam srpm: file with source package, MUST have a content type ``application/x-rpm``

- 

-     :resheader Location: contains URL to the created build

- 

-     :statuscode 201: build was successfully submitted

-     :statuscode 400: user data doesn't satisfy some requirements

-     :statuscode 403: authorization failed

- 

-     .. note::  Using a ``multipart/form-data`` might not be nice to read. To make your life a bit brighter, a Python example is included below.

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         POST /api_2/builds HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

-         Content-Length: xxxx

-         Content-Type: multipart/form-data; boundary=--------------------31063722920652

- 

-         ------------------------------31063722920652

-         Content-Disposition: form-data; name="metadata"

-         Content-Type: application/json

- 

-         {

-             "project_id": 3985,

-             "chroots": ["fedora-22-i386", "fedora-21-i386"],

-             "enable_net": false

-         }

- 

-         ------------------------------31063722920652

-         Content-Disposition: form-data; name="srpm"; filename="package-2.6-fc21.src.rpm"

-         Content-Type: application/x-rpm

- 

-         << SRPM BINARY CONTENT HERE >>

- 

-         -----------------------------31063722920652--

- 

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 201 CREATED

-         Location: /api_2/builds/106897

-         

- 

-     **Python Example**:

- 

-     Here we use python-requests_ lib:

- 

-     .. code-block:: python

- 

-         >>> import json

-         >>> from requests import post

-         >>> api_url = "http://copr.stg.fedoraproject.org/api_2/builds"

-         >>> api_login = "my api login"

-         >>> api_token = "my api token"

-         >>> metadata = {

-         >>>     'chroots': ['fedora-22-i386', 'fedora-21-i386'],

-         >>>     'project_id': 3985,

-         >>> }

-         >>> files = {

-         >>>     "srpm": ('pkg.src.rpm', open('/path/to/pkg.src.rpm'), 'application/x-rpm'),

-         >>> }

-         >>> data = {

-         >>>     "metadata": (json.dumps(metadata), 'application/json'),

-         >>> }

-         >>> r = post(api_url, auth=(api_login, api_token), files=files, data=data)

-         >>> r.status_code

-         201

-         >>> r.headers["Location"]

-         http://copr.stg.fedoraproject.org/api_2/builds/106899

- 

- Get build details

- -----------------

- 

- .. http:get:: /api_2/builds/(int:build_id)

- 

-     Returns details about build

- 

-     :param int build_id: a unique identifier of the build

-     :query bool show_build_tasks: embed :doc:`./build_task` sub-resources into the result, default is False

- 

-     :statuscode 200: no error

-     :statuscode 404: build not found

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/builds/106897?show_build_tasks=True HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "build_tasks": [

-             {

-               "tasks": {

-                 "build_id": 3985,

-                 "chroot_name": "fedora-21-i386",

-                 "started_on": 1441366860,

-                 "state": "succeeded",

-                 "ended_on": 1441366969,

-                 "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fedora-21-i386/00106897-copr-ping",

-                 "git_hash": "8daed2e23140243d8beaafb0fee436c1bca3fdf7"

-               },

-               "_links": {

-                 "project": {

-                   "href": "/api_2/projects/3985"

-                 },

-                 "self": {

-                   "href": "/api_2/build_tasks/106897/fedora-21-i386"

-                 }

-               }

-             }

-           ],

-           "_links": {

-             "project": {

-               "href": "/api_2/projects/3985"

-             },

-             "self": {

-               "href": "/api_2/builds/106897?show_chroots=True"

-             },

-             "build_tasks": {

-               "href": "/api_2/build_tasks/?build_id=3985"

-             }

-           },

-           "build": {

-             "enable_net": true,

-             "source_metadata": {

-               "url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"

-             },

-             "package_name": "copr-ping",

-             "submitted_on": 1441366834,

-             "package_version": "1-1.fc20",

-             "built_packages": [

-               {

-                 "version": "1",

-                 "name": "copr-ping"

-               }

-             ],

-             "started_on": null,

-             "source_type": "srpm_link",

-             "state": "succeeded",

-             "ended_on": 1441366969,

-             "id": 106897,

-             "repos": [],

-             "submitter": "asamalik"

-           }

-         }

- 

- Cancel build

- ------------

- 

- Build cancellation is done be setting build state to ``cancelled``.

- 

- .. http:put:: /api_2/builds/(int:build_id)

- 

-     **REQUIRE AUTH**

- 

-     :param int build_id: a unique identifier of the build

- 

-     :statuscode 204: build was updated

-     :statuscode 400: malformed request, most probably build can't be canceled at the moment

-     :statuscode 404: build not found

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         PUT /api_2/builds/1 HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

-         Content-Type: application/json

- 

-         {

-             "state": "cancelled"

-         }

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 204 NO CONTENT

- 

- Delete build

- ------------

- .. http:delete:: /api_2/builds/(int:build_id)

- 

-     **REQUIRE AUTH**

- 

-     Deletes build and schedules deletion of build result from the Copr backend

- 

-     :param int build_id: a unique identifier of the build

- 

-     :statuscode 204: build was removed

-     :statuscode 400: could not delete build right now, most probably due to unfinished build

-     :statuscode 403: authorization failed

-     :statuscode 404: build not found

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         DELETE /api_2/builds/1 HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 204 NO CONTENT

- 

- 

- .. _python-requests: http://docs.python-requests.org/

@@ -1,155 +0,0 @@ 

- Build Task

- ==========

- 

- Build task represents information about individual build tasks. One task is responsible for one chroot.

- 

- Structure of the build task entity

- ----------------------------------

- 

- .. code-block:: javascript

- 

-     {

-         "chroot_name": "fedora-rawhide-x86_64",

-         "build_id": 12345,

-         "started_on": 1440753865,

-         "ended_on": 1440753919,

-         "state": "succeeded",

-         "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fedora-rawhide-x86_64/00106882-python-marshmallow",

-         "git_hash": "d241064b14f9dcd5d9032d0aca3b4e78fbd1aafd"

-     }

- 

- Build tasks fields

- ~~~~~~~~~~~~~~~~~~

- ==================  ==================== ===============

- Field               Type                 Description

- ==================  ==================== ===============

- chroot_name         str                  chroot name

- build_id            int                  unique build identifier

- state               str                  current build task state

- started_on          int(unixtime UTC)    time when the build chroot started

- ended_on            int(unixtime UTC)    time when the build chroot ended

- git_hash            str                  hash of the git commit in dist-git used for the build

- result_dir_url      str(URL)             location of the build results

- ==================  ==================== ===============

- 

- 

- .. note::

-     Build Task doesn't currently support any modifications,

-     so all fields are read-only.

- 

- List build tasks

- ----------------

- 

- .. http:get:: /api_2/builds_tasks

- 

-     Returns list of build tasks according to the given query parameters

- 

-     :query str owner: select build tasks from projects owned by this user

-     :query int project_id:

-         select build tasks from one project,

-         when used query parameter ``owner`` is ignored

-     :query int build_id:

-         select build tasks from one project,

-         when used query parameters ``owner`` and ``project_id`` are ignored

-     :query str state:

-         select builds in particular state, allowed values:

-         ``failed``, ``succeeded``, ``canceled``, ``running``,

-         ``pending``, ``starting``, ``importing``

- 

-     :query int offset: offset number, default value is 0

-     :query int limit: limit number between 1 and 100, default value is 100

- 

-     :statuscode 200: no error

-     :statuscode 404: build not found

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/builds_tasks?build_id=106882 HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "build_tasks": [

-             {

-               "build_task": {

-                 "chroot_name": "fedora-rawhide-x86_64",

-                 "started_on": 1440753865,

-                 "state": "succeeded",

-                 "ended_on": 1440753919,

-                 "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fedora-rawhide-x86_64/00106882-python-marshmallow",

-                 "git_hash": "d241064b14f9dcd5d9032d0aca3b4e78fbd1aafd"

-               },

-               "_links": {

-                 "project": {

-                   "href": "/api_2/projects/3985"

-                 },

-                 "self": {

-                   "href": "/api_2/build_tasks/106882/fedora-rawhide-x86_64"

-                 }

-               }

-             }

-           ],

-           "_links": {

-             "self": {

-               "href": "/api_2/build_tasks?build_id=106882"

-             }

-           }

-         }

- 

- 

- 

- Get build task details

- ----------------------

- 

- .. http:get:: /api_2/build_tasks/(int:build_id)/(str:name)

- 

-     Returns details about one build task

- 

-     :param int build_id: a unique identifier of the build

-     :param str name: chroot name

- 

-     :statuscode 200: no error

-     :statuscode 404: build or build task not found

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/build_tasks/106882/fedora-rawhide-x86_64 HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "build_task": {

-             "chroot_name": "fedora-rawhide-x86_64",

-             "build_id": 3985,

-             "started_on": 1440753865,

-             "state": "succeeded",

-             "ended_on": 1440753919,

-             "result_dir_url": "http://copr-be-dev.cloud.fedoraproject.org/results/vgologuz/aeghqawgt/fedora-rawhide-x86_64/00106882-python-marshmallow",

-             "git_hash": "d241064b14f9dcd5d9032d0aca3b4e78fbd1aafd"

-           },

-           "_links": {

-             "project": {

-               "href": "/api_2/projects/3985"

-             },

-             "self": {

-               "href": "/api_2/build_tasks/106882/fedora-rawhide-x86_64"

-             }

-           }

-         }

- 

@@ -1,121 +0,0 @@ 

- Mock Chroot

- ===========

- 

- Mock chroot resources represents available chroots for builds. API provides only read-only access,

- since configuration of the build chroots is done by the service administrator.

- 

- Structure of the mock chroot entity

- -----------------------------------

- 

- .. code-block:: javascript

- 

-     {

-         "name": "epel-6-i386",

-         "os_release": "epel",

-         "os_version": "6",

-         "arch": "i386",

-         "is_active": true

-     }

- 

- Mock Chroot fields

- ~~~~~~~~~~~~~~~~~~

- ==================  ==================== ===============

- Field               Type                 Description

- ==================  ==================== ===============

- name                str                  chroot name

- os_release          str                  name of distribution system, e.g.: epel, fedora

- os_version          str                  version of distribution system, e.g.: 7, 22

- arch                str                  architecture of distribution, e.g.: i386, x86_64, ppc64le

- is_active           bool                 defines if this chroot is available for builds

- ==================  ==================== ===============

- 

- List mock chroots

- -----------------

- .. http:get:: /api_2/mock_chroots

- 

-     Returns a list of mock chroots

- 

-     :query param active_only: when set to True shows only active mock chroots

- 

-     :statuscode 200: no error

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         GET /api_2/mock_chroots?active_only=True HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "chroots": [

-             {

-               "chroot": {

-                 "name": "epel-6-i386",

-                 "os_release": "epel",

-                 "os_version": "6",

-                 "arch": "i386",

-                 "is_active": true

-               },

-               "_links": {

-                 "self": {

-                   "href": "/api_2/mock_chroots/epel-6-i386"

-                 }

-               }

-             },

-             {  },

-           ],

-           "_links": {

-             "self": {

-               "href": "/api_2/mock_chroots?active_only=True"

-             }

-           }

-         }

- 

- Get mock chroot details

- -----------------------

- .. http:get:: /api_2/mock_chroots/(str:chroot_name)

- 

-     Returns mock chroot details

- 

-     :param str chroot_name: Uniquer mock chroot name

- 

- 

-     :statuscode 200: no error

-     :statuscode 404: mock chroot not found by the given name

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/mock_chroots/fedora-rawhide-i386 HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "chroot": {

-             "name": "fedora-rawhide-i386",

-             "os_release": "fedora",

-             "os_version": "rawhide",

-             "arch": "i386",

-             "is_active": true

-           },

-           "_links": {

-             "self": {

-               "href": "/api_2/mock_chroots/fedora-rawhide-i386"

-             }

-           }

-         }

- 

@@ -1,306 +0,0 @@ 

- Project

- =======

- 

- Project resource represents copr projects and operations with them.

- 

- Structure of the project entity

- -------------------------------

- 

- **An example**:

- 

- .. code-block:: javascript

- 

-     {

-         "description": "A recent stable release of Ruby with Rails 3.2.8 and ... ",

-         "disable_createrepo": false,

-         "repos": [

-           "http://copr-be.cloud.fedoraproject.org/results/msuchy/scl-utils/epel-6-$basearch/",

-           "http://copr-be.cloud.fedoraproject.org/results/rhscl/httpd24/epel-6-$basearch/",

-         ],

-         "contact": null,

-         "owner": "rhscl",

-         "build_enable_net": true,

-         "instructions": "",

-         "homepage": null,

-         "id": 479,

-         "name": "ruby193"

-     }

- 

- 

- Project fields

- ~~~~~~~~~~~~~~

- ==================  ==================== ========= =================================================================================

- Field               Type                 Can edit? Description

- ==================  ==================== ========= =================================================================================

- id                  number               no        unique identifier

- owner               string               no        username of the project owner

- group               string               no        name of the group which owns the project, value is null for non-group projects

-                                                     - MAY be specified during a project creation to create a group managed project

- name                string               no        name of the project

-                                                     - MUST be specified during a project creation

- description         string               yes       project description

- instructions        string               yes       installation instructions

- homepage            string(URL)          yes       project homepage URL

- contact             string(URL or email) yes       contact with the project maintainer

- disable_createrepo  bool                 yes       disables automatic repository metadata generation

- build_enable_net    bool                 yes       set default value for new builds option `enable_net`

- repos               list of string       yes       list of additional repositories to be enabled during the build

- ==================  ==================== ========= =================================================================================

- 

- List projects

- -------------

- .. http:get:: /api_2/projects

- 

-     Returns a list of Copr projects according to the given query parameters

- 

-     :query str search_query: filter project using full-text search

-     :query str owner: select only projects owned by this user

-     :query str group: select only projects owned by this group

-     :query str name: select only projects with this name

-     :query int offset: offset number, default value is 0

-     :query int limit: limit number, default value is 100

- 

-     :statuscode 200: no error

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         GET /api_2/projects HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "_links": {

-             "self": {

-               "href": "/api_2/projects"

-             }

-           },

-           "projects": [

-             {

-               "project": {

-                 "description": "Lightweight buildsystem - upstream releases. Usually build few days before it land in Fedora.",

-                 "disable_createrepo": false,

-                 "repos": [],

-                 "contact": null,

-                 "owner": "msuchy",

-                 "build_enable_net": false,

-                 "instructions": "See https://pagure.io/copr/copr for more details.",

-                 "homepage": null,

-                 "id": 1,

-                 "name": "copr"

-               },

-               "_links": {   }

-             },

-           ]

-         }

- 

- Create new project

- ------------------

- .. http:post:: /api_2/projects

- 

-     **REQUIRE AUTH**

- 

-     Creates new Copr project.

- 

-     Additionally to what is described before in `Project fields`_, the user could specify a field `chroots`, which contains a list of chroots to be enabled.

-     Available `chroot` names could be obtained from :doc:`./mock_chroot`

- 

-     :resheader Location: contains URL to the newly created project entity

- 

-     :statuscode 201: project was successfully created

-     :statuscode 400: given data for project creation doesn't satisfy some requirements

-     :statuscode 401: the user already has project with the same name

-     :statuscode 403: authorization failed

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         POST /api_2/projects HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

-         Content-Type: application/json

- 

-         {

-             "disable_createrepo": false,

-             "build_enable_net": true,

-             "name": "foobar",

-             "chroots": [

-                 "fedora-22-x86_64",

-                 "fedora-22-i386",

-             ]

-         }

- 

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 201 CREATED

-         Location: /api_2/projects/<new project id>

- 

- 

- Get project details

- -------------------

- .. http:get:: /api_2/projects/(int:project_id)

- 

-     Returns details about given Copr project

- 

-     :param int project_id: a unique identifier of the Copr project.

- 

-     :query bool show_builds: include :doc:`./build` entities owned by this project in the result, default is False

-     :query bool show_chroots: include :doc:`./project_chroot` sub-resources in the result, default is False

- 

-     :statuscode 200: no error

-     :statuscode 404: project not found

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/projects/2482?show_chroots=True&show_builds=True HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-             "project": {

-                 "description": "A simple KDE respin",

-                 "disable_createrepo": false,

-                 "repos": [],

-                 "contact": null,

-                 "owner": "jmiahman",

-                 "build_enable_net": true,

-                 "instructions": "",

-                 "homepage": null,

-                 "id": 2482,

-                 "name": "Synergy-Linux"

-             },

-             "project_chroots": [

-                 {

-                     "chroot": {

-                         "comps": null,

-                         "comps_len": 0,

-                         "buildroot_pkgs": [],

-                         "name": "fedora-19-x86_64",

-                         "comps_name": null

-                     }

-                     "_links": {}

-                 },

-                 { }

-             ],

-             "project_builds": [

-                 {

-                     "_links": { },

-                     "build": {

-                         "enable_net": true,

-                         "source_metadata": {

-                             "url": "http://miroslav.suchy.cz/copr/copr-ping-1-1.fc20.src.rpm"

-                         },

-                         "submitted_on": 1422379448,

-                         "repos": [],

-                         "results": "https://copr-be.cloud.fedoraproject.org/results/jmiahman/Synergy-Linux/",

-                         "started_on": 1422379466,

-                         "source_type": 1,

-                         "state": "succeeded",

-                         "source_json": "{\"url\": \"http://dl.kororaproject.org/pub/korora/releases/21/source/korora-welcome-21.6-1.fc21.src.rpm\"}",

-                         "ended_on": 1422379584,

-                         "timeout": 21600,

-                         "pkg_version": "21.6-1.fc21",

-                         "id": 69493,

-                         "submitter": "asamalik"

-                     }

-                 },

-                 {  }

-             ],

-             "_links": {

-                 "self": {

-                   "href": "/api_2/projects/2482?show_builds=True&show_chroots=True"

-                 },

-                 "chroots": {

-                   "href": "/api_2/projects/2482/chroots"

-                 },

-                 "builds": {

-                   "href": "/api_2/builds?project_id=2482"

-                 }

-             }

-         }

- 

- Delete project

- --------------

- .. http:delete:: /api_2/projects/(int:project_id)

- 

-     **REQUIRE AUTH**

- 

-     Deletes Copr project

- 

-     :param project_id: a unique identifier of the Copr project.

- 

-     :statuscode 204: project was removed

-     :statuscode 400: could not delete build right now, most probably due to unfinished builds

-     :statuscode 403: authorization failed

-     :statuscode 404: project not found

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         DELETE /api_2/projects/1 HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 204 NO CONTENT

- 

- Modify project

- --------------

- .. http:put:: /api_2/projects/(int:project_id)

- 

-     **REQUIRE AUTH**

- 

-     Updates Copr project.

- 

-     .. note:: Project chroots can not be enabled or disabled here. Use :doc:`./project_chroot` resource for this purpose.

- 

-     :param project_id: a unique identifier of the Copr project.

- 

-     :statuscode 204: project was updated

-     :statuscode 400: malformed request, see response content for details

-     :statuscode 403: authorization failed

-     :statuscode 404: project not found

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         PUT /api_2/projects/1 HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

-         Content-Type: application/json

- 

-         {

-             "disable_createrepo": true,

-             "build_enable_net": false,

-             "instructions": "foobar"

-         }

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 204 NO CONTENT

@@ -1,239 +0,0 @@ 

- Project Chroot

- ==============

- 

- Projects Chroots allows to enable and disable target chroots and modify project settings dedicated for specific chroots.

- 

- Structure of the project chroot entity

- --------------------------------------

- 

- .. code-block:: javascript

- 

-     {

-         "comps": null,

-         "comps_len": 0,

-         "buildroot_pkgs": [

-             "scl-build-utils",

-             "foobar"

-         ],

-         "name": "fedora-22-i386",

-         "comps_name": null

-     }

- 

- Project Chroot fields

- ~~~~~~~~~~~~~~~~~~~~~

- ==================  ==================== ========= ===============

- Field               Type                 Can edit? Description

- ==================  ==================== ========= ===============

- name                string               no        chroot name

- buildroot_pkgs      list of strings      yes       packages to be installed into the buildroot

- comps               string               yes       content of the `comps.xml`_

- comps_name          string               yes       name of the uploaded comps file

- comps_len           int                  no        size of the uploaded comps file (bytes)

- ==================  ==================== ========= ===============

- 

- List project chroots

- --------------------

- .. http:get:: /api_2/projects/(int:project_id)/chroots

- 

-     Returns a list of project chroots

- 

-     :param int project_id: a unique identifier of the Copr project.

- 

-     :statuscode 200: no error

-     :statuscode 404: project not found

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/projects/2482/chroots HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "chroots": [

-             {

-               "chroot": {

-                 "comps": null,

-                 "comps_len": 0,

-                 "buildroot_pkgs": [],

-                 "name": "fedora-21-x86_64",

-                 "comps_name": null

-               },

-               "_links": {

-                 "project": {

-                   "href": "/api_2/projects/2482"

-                 },

-                 "self": {

-                   "href": "/api_2/projects/2482/chroots/fedora-21-x86_64"

-                 }

-               }

-             },

-             { }

-           ],

-           "_links": {

-             "self": {

-               "href": "/api_2/projects/2482/chroots"

-             }

-           }

-         }

- 

- Enable chroot for project

- -------------------------

- .. http:post:: /api_2/projects/(int:project_id)/chroots

- 

-     **REQUIRE AUTH**

- 

-     Enables chroot for the Copr project.

-     Available `chroot` names can be obtained from :doc:`./mock_chroot` resource.

- 

-     :param int project_id: a unique identifier of the Copr project.

- 

-     :resheader Location: contains URL to the enabled project chroot

- 

-     :statuscode 201: project was successfully created

-     :statuscode 400: given data doesn't satisfy some requirements

-     :statuscode 401: this chroot is already enabled

-     :statuscode 403: authorization failed

- 

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         POST  HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

-         Accept: application/json

- 

-         {

-             "buildroot_pkgs": ["foo", "bar"],

-             "name": "fedora-22-x86_64"

-         }

- 

-     **Response**:

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 201 CREATED

-         Location: /api_2/projects/2482/chroots/fedora-22-x86_64

- 

- Get project chroot details

- --------------------------

- .. http:get:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)

- 

-     Returns details about Copr project

- 

-     :param int project_id: a unique identifier of the Copr project.

-     :param str chroot_name: name of the project chroot

- 

-     :statuscode 200: no error

-     :statuscode 404: project not found or chroot isn't enabled for the project

- 

-     **Example request**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/projects/2482/chroots/fedora-22-x86_64 HTTP/1.1

-         Host: copr.fedoraproject.org

-         Accept: application/json

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 200 OK

-         Content-Type: application/json

- 

-         {

-           "chroot": {

-             "comps": null,

-             "comps_len": 0,

-             "buildroot_pkgs": [

-               "foo",

-               "bar"

-             ],

-             "name": "fedora-22-x86_64",

-             "comps_name": null

-           },

-           "_links": {

-             "project": {

-               "href": "/api_2/projects/2482"

-             },

-             "self": {

-               "href": "/api_2/projects/2482/chroots/fedora-22-x86_64"

-             }

-           }

-         }

- 

- Disable chroot for project

- --------------------------

- .. http:delete:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)

- 

-     **REQUIRE AUTH**

- 

-     Disables chroot for the Copr project

- 

-     :param int project_id: a unique identifier of the Copr project.

-     :param str chroot_name: name of the project chroot

- 

-     :statuscode 204: chroot was disabled

-     :statuscode 403: authorization failed

-     :statuscode 404: project not found or chroot isn't enabled for the project

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         DELETE /api_2/projects/2482/chroots/fedora-22-x86_64  HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 204 NO CONTENT

- 

- Modify project chroot

- ---------------------

- .. http:put:: /api_2/projects/(int:project_id)/chroots/(str:chroot_name)

- 

-     **REQUIRE AUTH**

- 

-     Updated project chroot settings

- 

-     :param int project_id: a unique identifier of the Copr project.

-     :param str chroot_name: name of the project chroot

- 

-     :statuscode 201: project chroot was updated

-     :statuscode 400: malformed request, see response content for details

-     :statuscode 403: authorization failed

-     :statuscode 404: project not found or chroot isn't enabled for the project

- 

-     **Example request**:

- 

-     .. sourcecode:: http

- 

-         PUT /api_2/projects/2482/chroots/fedora-22-x86_64  HTTP/1.1

-         Host: copr.fedoraproject.org

-         Authorization: Basic base64=encoded=string

- 

-         {

-             "buildroot_pkgs": []

-         }

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 204 NO CONTENT

- 

- 

- .. _comps.xml: https://fedorahosted.org/comps/

@@ -1,268 +0,0 @@ 

- # -*- coding: utf-8 -*-

- #

- # Copr Api 2 documentation build configuration file, created by

- # sphinx-quickstart on Wed Sep  2 14:50:00 2015.

- #

- # This file is execfile()d with the current directory set to its

- # containing dir.

- #

- # Note that not all possible configuration values are present in this

- # autogenerated file.

- #

- # All configuration values have a default; values that are commented out

- # serve to show the default.

- 

- import sys

- import os

- 

- # If extensions (or modules to document with autodoc) are in another directory,

- # add these directories to sys.path here. If the directory is relative to the

- # documentation root, use os.path.abspath to make it absolute, like shown here.

- #sys.path.insert(0, os.path.abspath('.'))

- 

- # -- General configuration ------------------------------------------------

- 

- # If your documentation needs a minimal Sphinx version, state it here.

- #needs_sphinx = '1.0'

- 

- # Add any Sphinx extension module names here, as strings. They can be

- # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom

- # ones.

- extensions = [

-     'sphinx.ext.autodoc',

-     'sphinx.ext.intersphinx',

-     'sphinx.ext.ifconfig',

-     'sphinx.ext.viewcode',

-     'sphinxcontrib.httpdomain',

- ]

- 

- # Add any paths that contain templates here, relative to this directory.

- templates_path = ['_templates']

- 

- # The suffix of source filenames.

- source_suffix = '.rst'

- 

- # The encoding of source files.

- #source_encoding = 'utf-8-sig'

- 

- # The master toctree document.

- master_doc = 'index'

- 

- # General information about the project.

- project = u'Copr Api 2'

- copyright = u'2017, https://pagure.io/copr/copr'

- 

- # The version info for the project you're documenting, acts as replacement for

- # |version| and |release|, also used in various other places throughout the

- # built documents.

- #

- # The short X.Y version.

- version = '0.1'

- # The full version, including alpha/beta/rc tags.

- release = '0.1'

- 

- # The language for content autogenerated by Sphinx. Refer to documentation

- # for a list of supported languages.

- #language = None

- 

- # There are two options for replacing |today|: either, you set today to some

- # non-false value, then it is used:

- #today = ''

- # Else, today_fmt is used as the format for a strftime call.

- #today_fmt = '%B %d, %Y'

- 

- # List of patterns, relative to source directory, that match files and

- # directories to ignore when looking for source files.

- exclude_patterns = []

- 

- # The reST default role (used for this markup: `text`) to use for all

- # documents.

- #default_role = None

- 

- # If true, '()' will be appended to :func: etc. cross-reference text.

- #add_function_parentheses = True

- 

- # If true, the current module name will be prepended to all description

- # unit titles (such as .. function::).

- #add_module_names = True

- 

- # If true, sectionauthor and moduleauthor directives will be shown in the

- # output. They are ignored by default.

- #show_authors = False

- 

- # The name of the Pygments (syntax highlighting) style to use.

- pygments_style = 'sphinx'

- 

- # A list of ignored prefixes for module index sorting.

- #modindex_common_prefix = []

- 

- # If true, keep warnings as "system message" paragraphs in the built documents.

- #keep_warnings = False

- 

- 

- # -- Options for HTML output ----------------------------------------------

- 

- # The theme to use for HTML and HTML Help pages.  See the documentation for

- # a list of builtin themes.

- html_theme = 'default'

- 

- # Theme options are theme-specific and customize the look and feel of a theme

- # further.  For a list of options available for each theme, see the

- # documentation.

- #html_theme_options = {}

- 

- # Add any paths that contain custom themes here, relative to this directory.

- #html_theme_path = []

- 

- # The name for this set of Sphinx documents.  If None, it defaults to

- # "<project> v<release> documentation".

- #html_title = None

- 

- # A shorter title for the navigation bar.  Default is the same as html_title.

- #html_short_title = None

- 

- # The name of an image file (relative to this directory) to place at the top

- # of the sidebar.

- #html_logo = None

- 

- # The name of an image file (within the static path) to use as favicon of the

- # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32

- # pixels large.

- #html_favicon = None

- 

- # Add any paths that contain custom static files (such as style sheets) here,

- # relative to this directory. They are copied after the builtin static files,

- # so a file named "default.css" will overwrite the builtin "default.css".

- html_static_path = ['_static']

- 

- # Add any extra paths that contain custom files (such as robots.txt or

- # .htaccess) here, relative to this directory. These files are copied

- # directly to the root of the documentation.

- #html_extra_path = []

- 

- # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,

- # using the given strftime format.

- #html_last_updated_fmt = '%b %d, %Y'

- 

- # If true, SmartyPants will be used to convert quotes and dashes to

- # typographically correct entities.

- #html_use_smartypants = True

- 

- # Custom sidebar templates, maps document names to template names.

- #html_sidebars = {}

- 

- # Additional templates that should be rendered to pages, maps page names to

- # template names.

- #html_additional_pages = {}

- 

- # If false, no module index is generated.

- #html_domain_indices = True

- 

- # If false, no index is generated.

- #html_use_index = True

- 

- # If true, the index is split into individual pages for each letter.

- #html_split_index = False

- 

- # If true, links to the reST sources are added to the pages.

- #html_show_sourcelink = True

- 

- # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.

- #html_show_sphinx = True

- 

- # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.

- #html_show_copyright = True

- 

- # If true, an OpenSearch description file will be output, and all pages will

- # contain a <link> tag referring to it.  The value of this option must be the

- # base URL from which the finished HTML is served.

- #html_use_opensearch = ''

- 

- # This is the file name suffix for HTML files (e.g. ".xhtml").

- #html_file_suffix = None

- 

- # Output file base name for HTML help builder.

- htmlhelp_basename = 'CoprApi2doc'

- 

- 

- # -- Options for LaTeX output ---------------------------------------------

- 

- latex_elements = {

- # The paper size ('letterpaper' or 'a4paper').

- #'papersize': 'letterpaper',

- 

- # The font size ('10pt', '11pt' or '12pt').

- #'pointsize': '10pt',

- 

- # Additional stuff for the LaTeX preamble.

- #'preamble': '',

- }

- 

- # Grouping the document tree into LaTeX files. List of tuples

- # (source start file, target name, title,

- #  author, documentclass [howto, manual, or own class]).

- latex_documents = [

-   ('index', 'CoprApi2.tex', u'Copr Api 2 Documentation',

-    u'https://pagure.io/copr/copr', 'manual'),

- ]

- 

- # The name of an image file (relative to this directory) to place at the top of

- # the title page.

- #latex_logo = None

- 

- # For "manual" documents, if this is true, then toplevel headings are parts,

- # not chapters.

- #latex_use_parts = False

- 

- # If true, show page references after internal links.

- #latex_show_pagerefs = False

- 

- # If true, show URL addresses after external links.

- #latex_show_urls = False

- 

- # Documents to append as an appendix to all manuals.

- #latex_appendices = []

- 

- # If false, no module index is generated.

- #latex_domain_indices = True

- 

- 

- # -- Options for manual page output ---------------------------------------

- 

- # One entry per manual page. List of tuples

- # (source start file, name, description, authors, manual section).

- man_pages = [

-     ('index', 'coprapi2', u'Copr Api 2 Documentation',

-      [u'https://pagure.io/copr/copr'], 1)

- ]

- 

- # If true, show URL addresses after external links.

- #man_show_urls = False

- 

- 

- # -- Options for Texinfo output -------------------------------------------

- 

- # Grouping the document tree into Texinfo files. List of tuples

- # (source start file, target name, title, author,

- #  dir menu entry, description, category)

- texinfo_documents = [

-   ('index', 'CoprApi2', u'Copr Api 2 Documentation',

-    u'https://pagure.io/copr/copr', 'CoprApi2', 'One line description of project.',

-    'Miscellaneous'),

- ]

- 

- # Documents to append as an appendix to all manuals.

- #texinfo_appendices = []

- 

- # If false, no module index is generated.

- #texinfo_domain_indices = True

- 

- # How to display URL addresses: 'footnote', 'no', or 'inline'.

- #texinfo_show_urls = 'footnote'

- 

- # If true, do not generate a @detailmenu in the "Top" node's menu.

- #texinfo_no_detailmenu = False

- 

- 

- # Example configuration for intersphinx: refer to the Python standard library.

- intersphinx_mapping = {'http://docs.python.org/': None}

@@ -1,141 +0,0 @@ 

- .. Copr Api 2 documentation master file, created by

-    sphinx-quickstart on Wed Sep  2 14:50:00 2015.

-    You can adapt this file completely to your liking, but it should at least

-    contain the root `toctree` directive.

- 

- Welcome to Copr Api 2's documentation!

- ======================================

- 

- Welcome to the documentation of the new REST-like API for the Copr build service.

- Almost all API calls are done using ``application/json`` ContentType.

- 

- Endpoint of the the API is ``/api_2``, public data is available without authorization.

- 

- To create new projects, submit builds and do other modification requests, you need to provide an API token using

- BasicAuth_ . The token can be obtained and renewed at the CoprAPI_ page.

- 

- Resources

- ---------

- .. toctree::

-    :maxdepth: 2

- 

-    Resources/project

-    Resources/project_chroot

-    Resources/build

-    Resources/build_task

-    Resources/mock_chroot

- 

- 

- HATEOAS

- -------

- 

- 

- This API implements HATEOAS_ in the very simple form: each entity is accompanied with a set of relative links

- to other related entities. HATEOAS makes API self discoverable, so you don't need to learn how to access sub-resources.

- Here is a short example with the content of API root:

- 

- **GET /api_2**

- 

- .. code-block:: javascript

- 

-    {

-      "_links": {

-        "mock_chroots": {

-          "href": "/api_2/mock_chroots"

-        },

-        "self": {

-          "href": "/api_2/"

-        },

-        "projects": {

-          "href": "/api_2/projects"

-        },

-        "builds": {

-          "href": "/api_2/builds"

-        }

-        "build_tasks": {

-          "href": "/api_2/build_tasks"

-        }

-    }

- 

- Response structure

- ------------------

- 

- Each entity is enveloped into a json dict and accompanied with set of HATEOAS references.

- GET requests would return the following structures:

- 

- **Collection structure**:

- 

- A collection provides information about several objects.

- 

- .. code-block:: javascript

- 

-     {

-         "_links" {

-             "self": {

-                 "href": "<url which was used to obtain current collection>"

-             },

-             "<relation name>": {

-                 "href": "<relation url>"

-             }

-         },

-         "<collection name>": [

-             {<individual 1>},

-             {<individual 2>},

-             {

-                 "_links": {...},

-                 "<entity name>": {<entity structure>}

-             }

-         ]

-     }

- 

- **Individual structure**:

- 

- An individual provides information about one object.

- 

- .. code-block:: javascript

- 

-     {

-         "_links": {

-             "self": {

-                 "href": "<url which was used to obtain the object>"

-             },

-             "<relation name>": {

-                 "href": "<relation url>"

-             }

-         },

-         "<entity name>": {<entity structure>}

-     }

- 

- Errors

- ______

- 

- To distinguish errors, we use standard HTTP codes: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.

- Additional information may be contained in the response body, which SHOULD have `application/json` Content-Type.

- Inside json object, there will always be a key ``message`` with error description. In some cases, an

- additional information would be present at key ``data``.

- 

-     **Example**

- 

-     .. sourcecode:: http

- 

-         GET /api_2/builds?project_id=999999999 HTTP/1.1

-         Host: copr.fedoraproject.org

- 

-     **Response**

- 

-     .. sourcecode:: http

- 

-         HTTP/1.1 404 NOT FOUND

-         Content-Type: application/json

- 

-         {

-           "message": "Project with id `999999999` not found",

-           "data": {

-             "project_id": 999999999

-           }

-         }

- 

- 

- .. _BasicAuth: https://en.wikipedia.org/wiki/Basic_access_authentication

- .. _CoprAPI: https://copr.fedoraproject.org/api

- .. _HATEOAS: https://en.wikipedia.org/wiki/HATEOAS

Per the schedule described here:
https://fedora-copr.github.io/posts/EOL-APIv1-APIv2

I am removing the APIv2 code from the frontend, which means, that the
Copr service will no longer understand APIv2 calls.

Build succeeded.

test.tar
Can you please check the coverage differences? (see the attached tar with cov logs)

Seems like this drops the coverage for send_update_comps() e.g., but many hits in builds_logic and coprs_logic.py. For the new misses, we should either keep it covered (using api3) or drop the corresponding useless code.

1 new commit added

  • frontend: remove unused functions after removing APIv2
2 years ago

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

1 new commit added

  • frontend: add test for updating chroot via APIv3
2 years ago

Good catch. I added a test for updating chroot which should call send_update_comps() method, and removed some functions that we no longer use.

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

Still, some (pretty important IMVHO) coverage misses, but we can try to take a look later. Thank you. +1

Commit 36349b2 fixes this pull-request

Pull-Request has been merged by praiskup

2 years ago

Commit 8944a68 fixes this pull-request

Pull-Request has been merged by praiskup

2 years ago

Commit 354441e fixes this pull-request

Pull-Request has been merged by praiskup

2 years ago
Metadata
Changes Summary 34
+0 -4
file changed
frontend/coprs_frontend/coprs/__init__.py
+0 -26
file changed
frontend/coprs_frontend/coprs/logic/builds_logic.py
+0 -4
file changed
frontend/coprs_frontend/coprs/logic/coprs_logic.py
+0 -8
file changed
frontend/coprs_frontend/coprs/logic/helpers.py
+0 -21
file changed
frontend/coprs_frontend/coprs/models.py
-94
file removed
frontend/coprs_frontend/coprs/rest_api/__init__.py
-148
file removed
frontend/coprs_frontend/coprs/rest_api/common.py
-67
file removed
frontend/coprs_frontend/coprs/rest_api/exceptions.py
-1
file removed
frontend/coprs_frontend/coprs/rest_api/resources/__init__.py
-228
file removed
frontend/coprs_frontend/coprs/rest_api/resources/build.py
-103
file removed
frontend/coprs_frontend/coprs/rest_api/resources/build_task.py
-52
file removed
frontend/coprs_frontend/coprs/rest_api/resources/mock_chroot.py
-174
file removed
frontend/coprs_frontend/coprs/rest_api/resources/project.py
-114
file removed
frontend/coprs_frontend/coprs/rest_api/resources/project_chroot.py
-211
file removed
frontend/coprs_frontend/coprs/rest_api/schemas.py
-100
file removed
frontend/coprs_frontend/coprs/rest_api/util.py
+1 -1
file changed
frontend/coprs_frontend/coprs/templates/_helpers.html
+0 -36
file changed
frontend/coprs_frontend/tests/coprs_test_case.py
-553
file removed
frontend/coprs_frontend/tests/test_api/test_build_r.py
-169
file removed
frontend/coprs_frontend/tests/test_api/test_build_task_r.py
-46
file removed
frontend/coprs_frontend/tests/test_api/test_mock_chroot_r.py
-307
file removed
frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
-473
file removed
frontend/coprs_frontend/tests/test_api/test_project_r.py
+35
file added
frontend/coprs_frontend/tests/test_apiv3/test_project_chroots.py
+0 -46
file changed
frontend/coprs_frontend/tests/test_logic/test_builds_logic.py
-177
file removed
frontend/docs/api_2/Makefile
-1
file removed
frontend/docs/api_2/requirements.txt
-455
file removed
frontend/docs/api_2/source/Resources/build.rst
-155
file removed
frontend/docs/api_2/source/Resources/build_task.rst
-121
file removed
frontend/docs/api_2/source/Resources/mock_chroot.rst
-306
file removed
frontend/docs/api_2/source/Resources/project.rst
-239
file removed
frontend/docs/api_2/source/Resources/project_chroot.rst
-268
file removed
frontend/docs/api_2/source/conf.py
-141
file removed
frontend/docs/api_2/source/index.rst