Michal Koutný ccf7f1
# This work is licensed under the GNU GPLv2 or later.
Michal Koutný ccf7f1
# See the COPYING file in the top-level directory.
Michal Koutný ccf7f1
Michal Koutný ccf7f1
import base64
Michal Koutný ccf7f1
import json
Michal Koutný ccf7f1
import logging
Michal Koutný ccf7f1
import os
Michal Koutný ccf7f1
Michal Koutný ccf7f1
from ._backendbase import _BackendBase
Michal Koutný ccf7f1
from .exceptions import BugzillaError
Michal Koutný ccf7f1
from ._util import listify
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
log = logging.getLogger(__name__)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
def _update_key(indict, updict, key):
Michal Koutný ccf7f1
    if key not in indict:
Michal Koutný ccf7f1
        indict[key] = {}
Michal Koutný ccf7f1
    indict[key].update(updict.get(key, {}))
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
class _BackendREST(_BackendBase):
Michal Koutný ccf7f1
    """
Michal Koutný ccf7f1
    Internal interface for direct calls to bugzilla's REST API
Michal Koutný ccf7f1
    """
Michal Koutný ccf7f1
    def __init__(self, url, bugzillasession):
Michal Koutný ccf7f1
        _BackendBase.__init__(self, url, bugzillasession)
Michal Koutný ccf7f1
        self._bugzillasession.set_rest_defaults()
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    #########################
Michal Koutný ccf7f1
    # Internal REST helpers #
Michal Koutný ccf7f1
    #########################
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def _handle_response(self, text):
Michal Koutný ccf7f1
        try:
Michal Koutný ccf7f1
            ret = dict(json.loads(text))
Michal Koutný ccf7f1
        except Exception:  # pragma: no cover
Michal Koutný ccf7f1
            log.debug("Failed to parse REST response. Output is:\n%s", text)
Michal Koutný ccf7f1
            raise
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        if ret.get("error", False):
Michal Koutný ccf7f1
            raise BugzillaError(ret["message"], code=ret["code"])
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def _op(self, method, apiurl, paramdict=None):
Michal Koutný ccf7f1
        fullurl = os.path.join(self._url, apiurl.lstrip("/"))
Michal Koutný ccf7f1
        log.debug("Bugzilla REST %s %s params=%s", method, fullurl, paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        data = None
Michal Koutný ccf7f1
        authparams = self._bugzillasession.get_auth_params()
Michal Koutný ccf7f1
        if method == "GET":
Michal Koutný ccf7f1
            authparams.update(paramdict or {})
Michal Koutný ccf7f1
        else:
Michal Koutný ccf7f1
            data = json.dumps(paramdict or {})
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        response = self._bugzillasession.request(method, fullurl, data=data,
Michal Koutný ccf7f1
                params=authparams)
Michal Koutný ccf7f1
        return self._handle_response(response.text)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def _get(self, *args, **kwargs):
Michal Koutný ccf7f1
        return self._op("GET", *args, **kwargs)
Michal Koutný ccf7f1
    def _put(self, *args, **kwargs):
Michal Koutný ccf7f1
        return self._op("PUT", *args, **kwargs)
Michal Koutný ccf7f1
    def _post(self, *args, **kwargs):
Michal Koutný ccf7f1
        return self._op("POST", *args, **kwargs)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    #######################
Michal Koutný ccf7f1
    # API implementations #
Michal Koutný ccf7f1
    #######################
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def get_xmlrpc_proxy(self):
Michal Koutný ccf7f1
        raise BugzillaError("You are using the bugzilla REST API, "
Michal Koutný ccf7f1
                "so raw XMLRPC access is not provided.")
Michal Koutný ccf7f1
    def is_rest(self):
Michal Koutný ccf7f1
        return True
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bugzilla_version(self):
Michal Koutný ccf7f1
        return self._get("/version")
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_create(self, paramdict):
Michal Koutný ccf7f1
        return self._post("/bug", paramdict)
Michal Koutný ccf7f1
    def bug_fields(self, paramdict):
Michal Koutný ccf7f1
        return self._get("/field/bug", paramdict)
Michal Koutný ccf7f1
    def bug_get(self, bug_ids, aliases, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["id"] = listify(bug_ids)
Michal Koutný ccf7f1
        data["alias"] = listify(aliases)
Michal Koutný ccf7f1
        ret = self._get("/bug", data)
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_attachment_get(self, attachment_ids, paramdict):
Michal Koutný ccf7f1
        # XMLRPC supported mutiple fetch at once, but not REST
Michal Koutný ccf7f1
        ret = {}
Michal Koutný ccf7f1
        for attid in listify(attachment_ids):
Michal Koutný ccf7f1
            out = self._get("/bug/attachment/%s" % attid, paramdict)
Michal Koutný ccf7f1
            _update_key(ret, out, "attachments")
Michal Koutný ccf7f1
            _update_key(ret, out, "bugs")
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_attachment_get_all(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        # XMLRPC supported mutiple fetch at once, but not REST
Michal Koutný ccf7f1
        ret = {}
Michal Koutný ccf7f1
        for bugid in listify(bug_ids):
Michal Koutný ccf7f1
            out = self._get("/bug/%s/attachment" % bugid, paramdict)
Michal Koutný ccf7f1
            _update_key(ret, out, "attachments")
Michal Koutný ccf7f1
            _update_key(ret, out, "bugs")
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_attachment_create(self, bug_ids, data, paramdict):
Michal Koutný ccf7f1
        if data is not None and "data" not in paramdict:
Michal Koutný ccf7f1
            paramdict["data"] = base64.b64encode(data).decode("utf-8")
Michal Koutný ccf7f1
        paramdict["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._post("/bug/%s/attachment" % paramdict["ids"][0],
Michal Koutný ccf7f1
                paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_attachment_update(self, attachment_ids, paramdict):
Michal Koutný ccf7f1
        paramdict["ids"] = listify(attachment_ids)
Michal Koutný ccf7f1
        return self._put("/bug/attachment/%s" % paramdict["ids"][0], paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_comments(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        # XMLRPC supported mutiple fetch at once, but not REST
Michal Koutný ccf7f1
        ret = {}
Michal Koutný ccf7f1
        for bugid in bug_ids:
Michal Koutný ccf7f1
            out = self._get("/bug/%s/comment" % bugid, paramdict)
Michal Koutný ccf7f1
            _update_key(ret, out, "bugs")
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
    def bug_history(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        # XMLRPC supported mutiple fetch at once, but not REST
Michal Koutný ccf7f1
        ret = {"bugs": []}
Michal Koutný ccf7f1
        for bugid in bug_ids:
Michal Koutný ccf7f1
            out = self._get("/bug/%s/history" % bugid, paramdict)
Michal Koutný ccf7f1
            ret["bugs"].extend(out.get("bugs", []))
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_search(self, paramdict):
Michal Koutný ccf7f1
        return self._get("/bug", paramdict)
Michal Koutný ccf7f1
    def bug_update(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._put("/bug/%s" % data["ids"][0], data)
Michal Koutný ccf7f1
    def bug_update_tags(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        raise BugzillaError("No REST API available for bug_update_tags")
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def component_create(self, paramdict):
Michal Koutný ccf7f1
        return self._post("/component", paramdict)
Michal Koutný ccf7f1
    def component_update(self, paramdict):
Michal Koutný ccf7f1
        if "ids" in paramdict:
Michal Koutný ccf7f1
            apiurl = str(listify(paramdict["ids"])[0])  # pragma: no cover
Michal Koutný ccf7f1
        if "names" in paramdict:
Michal Koutný ccf7f1
            apiurl = ("%(product)s/%(component)s" %
Michal Koutný ccf7f1
                    listify(paramdict["names"])[0])
Michal Koutný ccf7f1
        return self._put("/component/%s" % apiurl, paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def externalbugs_add(self, paramdict):  # pragma: no cover
Michal Koutný ccf7f1
        raise BugzillaError(
Michal Koutný ccf7f1
            "No REST API available yet for externalbugs_add")
Michal Koutný ccf7f1
    def externalbugs_remove(self, paramdict):  # pragma: no cover
Michal Koutný ccf7f1
        raise BugzillaError(
Michal Koutný ccf7f1
            "No REST API available yet for externalbugs_remove")
Michal Koutný ccf7f1
    def externalbugs_update(self, paramdict):  # pragma: no cover
Michal Koutný ccf7f1
        raise BugzillaError(
Michal Koutný ccf7f1
            "No REST API available yet for externalbugs_update")
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def group_get(self, paramdict):
Michal Koutný ccf7f1
        return self._get("/group", paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def product_get(self, paramdict):
Michal Koutný ccf7f1
        return self._get("/product/get", paramdict)
Michal Koutný ccf7f1
    def product_get_accessible(self):
Michal Koutný ccf7f1
        return self._get("/product_accessible")
Michal Koutný ccf7f1
    def product_get_enterable(self):
Michal Koutný ccf7f1
        return self._get("/product_enterable")
Michal Koutný ccf7f1
    def product_get_selectable(self):
Michal Koutný ccf7f1
        return self._get("/product_selectable")
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def user_create(self, paramdict):
Michal Koutný ccf7f1
        return self._post("/user", paramdict)
Michal Koutný ccf7f1
    def user_get(self, paramdict):
Michal Koutný ccf7f1
        return self._get("/user", paramdict)
Michal Koutný ccf7f1
    def user_login(self, paramdict):
Michal Koutný ccf7f1
        return self._get("/login", paramdict)
Michal Koutný ccf7f1
    def user_logout(self):
Michal Koutný ccf7f1
        return self._get("/logout")
Michal Koutný ccf7f1
    def user_update(self, paramdict):
Michal Koutný ccf7f1
        urlid = None
Michal Koutný ccf7f1
        if "ids" in paramdict:
Michal Koutný ccf7f1
            urlid = listify(paramdict["ids"])[0]  # pragma: no cover
Michal Koutný ccf7f1
        if "names" in paramdict:
Michal Koutný ccf7f1
            urlid = listify(paramdict["names"])[0]
Michal Koutný ccf7f1
        return self._put("/user/%s" % urlid, paramdict)