"""Provides a generic connector for upstream GraphQL services.""" from sgqlc.endpoint.requests import RequestsEndpoint from payee.config import Config from payee.constants.constants import GRAPHQL_ROUTER_URL 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 = RequestsEndpoint( 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') self.code = None self.response = None 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') # Make sure you construct an exception message with the # superclass for nice output in Sentry / State Machine etc. super().__init__( (f'{self.message} - {self.path} {self.code or ""} {self.response or ""}') ) def get_response_status(self): """Return service response status code. Returns: int """ if self.response: return self.response.get('status') # If the response is not populated something went wrong return 500 def get_response_body(self): """Return service response body. Returns: dict """ if self.response: return self.response.get('body') return {} def get_stacktrace(self): """Return service stacktrace. Returns: list """ return self.stacktrace def get_response(self): """Return service response. Returns: dict """ return self.response def get_graphql_client(env, service_name): """Get a graphql client. Args: env (str): Environment service_name (str): Service name Returns: dict """ graphql_router = GraphQLConnector(GRAPHQL_ROUTER_URL.format(env=env), service_name) return graphql_router graphql_client = get_graphql_client(Config.ENVIRONMENT, Config.SERVICE_NAME)