"""Provides a generic connector for upstream GraphQL services.""" from sgqlc.endpoint.http import HTTPEndpoint import backoff # from connectors.logging import logger as log class GraphQLConnector: """Connector to an upstream GraphQL service.""" def __init__(self, url: str, app_name: str, raise_on_error=True): """Create an upstream server instance.""" self.raise_on_error = raise_on_error self.endpoint = HTTPEndpoint( url, base_headers={'apollographql-client-name': app_name} ) self.headers = {} def set_headers(self, headers=None): """Set request headers. Args: headers (dict): headers """ self.headers.update(headers) def get_headers(self): """Return request headers. Returns: dict """ return self.headers def execute(self, query: str, data: dict): """Execute a call to a GraphQL endpoint. Args: query (str): Query data (dict): JSON formatted params Returns: dict """ response = self.endpoint( query, data, extra_headers=self.get_headers()) if self.raise_on_error and response.get('errors'): raise GraphQLError(response.get('errors')) return response class GraphQLError(Exception): """Any GraphQL errors.""" def __init__(self, errors): """Init. Args: errors (list): """ self.errors = errors error = errors.pop() self.message = error.get('message') self.path = error.get('path') extensions = error.get('extensions') if extensions: self.code = extensions.get('code') self.response = extensions.get('response') exception = extensions.get('exception') if exception: self.stacktrace = exception.get('stacktrace') def get_response_status(self): """Return service response status code. Returns: int """ return self.response.get('status') def get_response_body(self): """Return service response body. Returns: dict """ return self.response.get('body') def get_stacktrace(self): """Return service stacktrace. Returns: list """ return self.stacktrace def get_response(self): """Return service response. Returns: dict """ return self.response class GraphQLBackoffConnector(GraphQLConnector): """Connector to an upstream GraphQL service with backoff.""" @backoff.on_exception(backoff.expo, GraphQLError, max_tries=3, max_time=90, max_value=30) def execute(self, query: str, data: dict): """Execute a call to a GraphQL endpoint. Args: query (str): Query data (dict): JSON formatted params Returns: dict """ response = self.endpoint( query, data, extra_headers=self.get_headers()) if self.raise_on_error and response.get('errors'): raise GraphQLError(response.get('errors')) return response