Source code for sortinghat.cli.client.client

# -*- coding: utf-8 -*-
#
# Copyright (C) 2014-2020 Bitergia
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
#     Santiago DueƱas <sduenas@bitergia.com>
#

import requests
import uri

from sgqlc.endpoint.http import HTTPEndpoint
from sgqlc.operation import Operation

from .schema import sh_schema


[docs]class SortingHatClientError(Exception): """SortingHat client error. Generic exception raised by the client when an error is found either with connection problems or with GraphQL queries. :param msg: message error :param errors: list of GraphQL errors; `None` when the error is not related to GraphQL queries """ def __init__(self, msg, errors=None): self.msg = msg self.errors = errors def __str__(self): return self.msg
[docs]class SortingHatClient: """SortingHat client. This client allows to run operations in the SortingHat server that listens in `host` and `port`. After initializing an instance, call to `connect` to establish a communication with the server. The method `execute` allows to run queries and mutations defined by the server. :param host: host of the server :param port: port number used in the connection :param path: path to the API endpoint; by default the endpoint is in '/' :param user: user name to use when authentication is required :param password: password to use when authentication is required :param ssl: use SSL/TSL connection; this is the default behaviour :raises ValueError: when any of the given parameters is invalid """ def __init__(self, host, port=9314, path=None, user=None, password=None, ssl=True): self.gqlc = None self.host = host self.port = port if not path: self.path = '/' elif not path.startswith('/'): self.path = '/' + path else: self.path = path self.user = user self.password = password try: scheme = 'https' if ssl else 'http' self.url = uri.URI(scheme=scheme, host=self.host, port=self.port, path=self.path) except (ValueError, TypeError) as exc: msg = "Invalid URL parameters; cause: {}".format(exc) raise ValueError(msg)
[docs] def connect(self): """Establish a connection to the server.""" try: result = requests.get(self.url, headers={'Accept': 'text/html'}) result.raise_for_status() except requests.exceptions.RequestException as exc: msg = "Connection error; cause: {}".format(exc) raise SortingHatClientError(msg) headers = { 'X-CSRFToken': result.cookies['csrftoken'], 'Cookie': 'csrftoken=' + result.cookies['csrftoken'] } self.gqlc = HTTPEndpoint(self.url, headers) if self.user and self.password: op = Operation(sh_schema.SortingHatMutation) op.token_auth(username=self.user, password=self.password).token() result = self.gqlc(op) if 'errors' in result: cause = result['errors'][0]['message'] msg = "Authentication error; cause: {}".format(cause) raise SortingHatClientError(msg) auth_token = result['data']['tokenAuth']['token'] headers['Authorization'] = "JWT {}".format(auth_token)
[docs] def disconnect(self): """Disconnect the client from the server.""" self.gqlc = None
[docs] def execute(self, operation): """Execute operations in the server. This method allows to run GraphQL operations: queries and mutations. To run an operation, use `sgqlc.operation.Operation` object and a valid SortingHat schema. :param operation: GraphQL operation to execute :returns: a dict that maps the JSON result returned by the server :raises SortingHatClient: raised when either the client is not connected or when an error is returned while running the operation """ if not self.gqlc: msg = "Client not connected with {}; call connect() before executing any operation" raise SortingHatClientError(msg.format(self.url)) result = self.gqlc(operation) if 'errors' in result: msg = "GraphQL operation error; {} errors found".format(len(result['errors'])) raise SortingHatClientError(msg, errors=result['errors']) return result