Skip to content

Commit

Permalink
[python] support APIv2
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentin Gologuzov committed Oct 19, 2015
1 parent f03b309 commit 89cf42e
Show file tree
Hide file tree
Showing 25 changed files with 2,020 additions and 9 deletions.
5 changes: 4 additions & 1 deletion frontend/coprs_frontend/coprs/rest_api/resources/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def handle_post_json(self, req):
.format(err))
except InsufficientRightsException as err:
db.session.rollback()
raise AccessForbidden("User {} cannon create build in project {}: {}"
raise AccessForbidden("User {} cannot create build in project {}: {}"
.format(flask.g.user.username,
project.full_name, err))
return build.id
Expand Down Expand Up @@ -147,6 +147,9 @@ def handle_post_multipart(req):
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:
Expand Down
1 change: 1 addition & 0 deletions frontend/coprs_frontend/coprs/rest_api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class ProjectCreateSchema(ProjectSchema):
chroots = SpaceSeparatedList(load_only=True, default=list)


# todo: rename to ProjectChrootSchema
class CoprChrootSchema(Schema):

buildroot_pkgs = SpaceSeparatedList()
Expand Down
2 changes: 1 addition & 1 deletion frontend/docs/api_2/source/Resources/build.rst
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ MUST be uploaded as binary ``srpm`` file.
Field Type Description
================== ==================== ===============
project_id int identifier of the parent project
chroots list of strings what chroots should be used for build
chroots list of strings which chroots should be used for build
enable_net bool allows to disable network access during the build, default: True
================== ==================== ===============

Expand Down
5 changes: 5 additions & 0 deletions python/copr/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
from .client import CoprClient

from .client_v2.client import CoprClient as ClientV2

create_client2_from_params = ClientV2.create_from_params
create_client2_from_file_config = ClientV2.create_from_file_config
3 changes: 3 additions & 0 deletions python/copr/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
__description__ = "Python client for copr service."

from .client import CoprClient

# for cli backward compatibility
import copr.exceptions as exceptions
4 changes: 2 additions & 2 deletions python/copr/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def emit(self, record):
log = logging.getLogger(__name__)
log.addHandler(NullHandler())

from .exceptions import CoprConfigException, CoprNoConfException, \
from ..exceptions import CoprConfigException, CoprNoConfException, \
CoprRequestException, \
CoprUnknownResponseException

Expand All @@ -44,7 +44,7 @@ def emit(self, record):
CommonMsgErrorOutParser, NewBuildListParser, ProjectChrootsParser, \
ProjectDetailsFieldsParser

from .util import UnicodeMixin
from ..util import UnicodeMixin

# TODO: add deco to check that login/token are provided
# and raise correct error
Expand Down
2 changes: 1 addition & 1 deletion python/copr/client/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# TODO: Add Response for collections?
# TODO: what about copr_project-> chroot_list, build_list, repos_list
#
from .util import UnicodeMixin
from ..util import UnicodeMixin


class CoprResponse(UnicodeMixin):
Expand Down
1 change: 1 addition & 0 deletions python/copr/client_v2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# coding: utf-8
231 changes: 231 additions & 0 deletions python/copr/client_v2/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# coding: utf-8

from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import
from abc import ABCMeta, abstractproperty

import json
import sys
import os
import logging
import weakref
from marshmallow import pprint

import six
from six import with_metaclass
from six.moves import configparser

# from requests_toolbelt.multipart.encoder import MultipartEncoder

from .resources import Root
from .handlers import ProjectHandle, ProjectChrootHandle, BuildHandle, MockChrootHandle
from .common import EntityTypes
from .net_client import NetClient

if sys.version_info < (2, 7):
class NullHandler(logging.Handler):
def emit(self, record):
pass
else:
from logging import NullHandler

from ..exceptions import CoprConfigException, CoprNoConfException, \
CoprRequestException, \
CoprUnknownResponseException

from ..util import UnicodeMixin

log = logging.getLogger(__name__)
log.addHandler(NullHandler())


class HandlersProvider(with_metaclass(ABCMeta)):

@abstractproperty
def projects(self):
"""
:rtype: ProjectHandle
"""
pass

@abstractproperty
def project_chroots(self):
"""
:rtype: ProjectChrootHandle
"""
pass

@abstractproperty
def builds(self):
"""
:rtype: BuildHandle
"""
pass

# @abstractproperty
# def build_tasks(self):
# pass

@abstractproperty
def mock_chroots(self):
"""
:rtype: MockChrootHandle
"""
pass



class CoprClient(UnicodeMixin, HandlersProvider):
""" Main interface to the copr service
:param NetClient net_client: wrapper for http requests
:param unicode root_url: used as copr projects root
:param bool no_config: helper flag to indicate that no config was provided
Could be created:
- using static method :py:meth:`CoprClient.create_from_file_config`
- using static method :py:meth:`CoprClient.create_from_params`
If you create Client directly call post_init() method after the creation.
"""

def __init__(self, net_client, root_url=None, no_config=False):
"""
"""
self.nc = net_client
self.root_url = root_url or u"http://copr.fedoraproject.org"

self.no_config = no_config
self._post_init_done = False

self._main_resources = None

self.root = None

self._projects = None
self._project_chroots = None
self._builds = None
self._mock_chroots = None

def _check_client_init(self):
if not self._post_init_done:
raise RuntimeError("CoprClient wasn't initialized, use class-methods "
"create_from* to get instance of CoprClient")

@property
def projects(self):
self._check_client_init()
return self._projects

@property
def project_chroots(self):
self._check_client_init()
return self._project_chroots

@property
def builds(self):
self._check_client_init()
return self._builds

@property
def mock_chroots(self):
self._check_client_init()
return self._mock_chroots

def __unicode__(self):
return (
u"<Copr client. api root url: {}, config provided: {}, net client: {}>"
.format(self.root_url, not self.no_config, self.nc)
)

@property
def api_root(self):
"""
Url to API endpoint
"""
return "{0}/api_2".format(self.root_url)

@classmethod
def create_from_params(cls, root_url=None, login=None, token=None):
nc = NetClient(login, token)
client = cls(nc, root_url, no_config=True)
client.post_init()
return client


@classmethod
def create_from_file_config(cls, filepath=None, ignore_error=False):
"""
Creates Copr client using the information from the config file.
:param filepath: specifies config location,
default: "~/.config/copr"
:type filepath: `str`
:param bool ignore_error: When true and config is missing,
creates default Client without credentionals
:rtype: :py:class:`~.client.CoprClient`
"""
raw_config = configparser.ConfigParser()
if not filepath:
filepath = os.path.join(os.path.expanduser("~"), ".config", "copr")
config = {}
if not raw_config.read(filepath):
log.warning(
"No configuration file '~/.config/copr' found. "
"See man copr-cli for more information")
config["no_config"] = True
if not ignore_error:
raise CoprNoConfException()
else:
return cls.create_from_params()
else:
try:
for field in ["login", "token", "copr_url"]:
if six.PY3:
config[field] = raw_config["copr-cli"].get(field, None)
else:
config[field] = raw_config.get("copr-cli", field, None)
nc = NetClient(config["login"], config["token"])
client = cls(nc, root_url=config["copr_url"], )

except configparser.Error as err:
if not ignore_error:
raise CoprConfigException(
"Bad configuration file: {0}".format(err))
else:
return cls.create_from_params()

client.post_init()
return client

def post_init(self):

log.debug("Getting root resources")

response = self.nc.request(self.api_root)

# obtain api info info
self.root = Root.from_response(response, self.root_url)

# instantiating handlers
self._projects = ProjectHandle(
weakref.proxy(self), self.nc, root_url=self.root_url,
projects_href=self.root.get_href_by_name(u"projects"),)
self._project_chroots = ProjectChrootHandle(
weakref.proxy(self), self.nc, root_url=self.root_url)

self._builds = BuildHandle(
weakref.proxy(self), self.nc, root_url=self.root_url,
builds_href=self.root.get_href_by_name(u"builds"),)
# self._build_chroots = BuildTaskHandle()

self._mock_chroots = MockChrootHandle(
weakref.proxy(self), self.nc, root_url=self.root_url,
href=self.root.get_href_by_name(u"mock_chroots")
)

self._post_init_done = True
21 changes: 21 additions & 0 deletions python/copr/client_v2/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# coding: utf-8
from copr.util import UnicodeMixin


class EntityTypes(object):
ROOT = "root"
PROJECT = "project"
PROJECT_CHROOT = "project_chroot"
BUILD = "build"
BUILD_TASK = "build_task"
MOCK_CHROOT = "mock_chroot"


class BuiltPackage(UnicodeMixin):

def __init__(self, name, version):
self.name = name
self.version = version

def __unicode__(self):
return u"{} {}".format(self.name, self.version)

0 comments on commit 89cf42e

Please sign in to comment.