def __init__(self, security=None, **kwargs): """ Init the ESI client object :param security: (optional) the security object [default: None] :param headers: (optional) additional headers we want to add :param transport_adapter: (optional) an HTTPAdapter object / implement :param cache: (optional) esipy.cache.BaseCache cache implementation. :param raw_body_only: (optional) default value [False] for all requests """ super(EsiClient, self).__init__(security) # lets get rid of all our kwargs headers = kwargs.pop('headers', {}) transport_adapter = kwargs.pop('transport_adapter', None) # initiate the cache object if 'cache' not in kwargs: self.cache = DictCache() else: cache = kwargs.pop('cache') if isinstance(cache, BaseCache): self.cache = cache elif cache is None: self.cache = DummyCache() else: raise ValueError('Provided cache must implement BaseCache') # store default raw_body_only in case user never want parsing self.raw_body_only = kwargs.pop('raw_body_only', False) self.timeout = kwargs.pop('timeout', 10) self.security = security self._session = Session() # check for specified headers and update session.headers if 'User-Agent' not in headers: headers['User-Agent'] = ('EsiPy/Client - ' 'https://github.com/Kyria/EsiPy') self._session.headers.update({"Accept": "application/json"}) self._session.headers.update(headers) # transport adapter if isinstance(transport_adapter, HTTPAdapter): self._session.mount('http://', transport_adapter) self._session.mount('https://', transport_adapter)
def get_esi_client_for_account(token: Optional[SSOToken], noauth: bool = False, retry_request: bool = False) -> EsiClient: if noauth: return EsiClient(timeout=20, headers={'User-Agent': config.user_agent}, cache=DummyCache(), retry_requests=retry_request) signal: Signal = Signal() signal.add_receiver(SSOToken.update_token_callback) security = EsiSecurity(crest_return_url, crest_client_id, crest_client_secret, headers={'User-Agent': config.user_agent}, signal_token_updated=signal, token_identifier=token.tokenID) security.update_token(token.info_for_esi_security()) return EsiClient(security, timeout=20, headers={'User-Agent': config.user_agent}, cache=DummyCache(), retry_requests=retry_request)
class TestDummyCache(BaseTest): """ DummyCache test class. """ def setUp(self): self.c = DummyCache() self.c.set('never_stored', True) def test_dummy_cache_set(self): self.assertNotIn('never_stored', self.c._dict) def test_dummy_cache_get(self): self.assertEqual(self.c.get('never_stored'), None) def test_dummy_cache_invalidate(self): self.c.invalidate('never_stored') self.assertIsNone(self.c.get('never_stored'))
def setUp(self): self.c = DummyCache() self.c.set('never_stored', True)
class EsiClient(BaseClient): __schemes__ = {'https'} __image_server__ = { 'singularity': 'https://image.testeveonline.com/', 'tranquility': 'https://imageserver.eveonline.com/', } def __init__(self, security=None, **kwargs): """ Init the ESI client object :param security: (optional) the security object [default: None] :param headers: (optional) additional headers we want to add :param transport_adapter: (optional) an HTTPAdapter object / implement :param cache: (optional) esipy.cache.BaseCache cache implementation. :param raw_body_only: (optional) default value [False] for all requests """ super(EsiClient, self).__init__(security) # lets get rid of all our kwargs headers = kwargs.pop('headers', {}) transport_adapter = kwargs.pop('transport_adapter', None) # initiate the cache object if 'cache' not in kwargs: self.cache = DictCache() else: cache = kwargs.pop('cache') if isinstance(cache, BaseCache): self.cache = cache elif cache is None: self.cache = DummyCache() else: raise ValueError('Provided cache must implement BaseCache') # store default raw_body_only in case user never want parsing self.raw_body_only = kwargs.pop('raw_body_only', False) self.timeout = kwargs.pop('timeout', 10) self.security = security self._session = Session() # check for specified headers and update session.headers if 'User-Agent' not in headers: headers['User-Agent'] = ('EsiPy/Client - ' 'https://github.com/Kyria/EsiPy') self._session.headers.update({"Accept": "application/json"}) self._session.headers.update(headers) # transport adapter if isinstance(transport_adapter, HTTPAdapter): self._session.mount('http://', transport_adapter) self._session.mount('https://', transport_adapter) def request(self, req_and_resp, raw_body_only=None, opt=None): """ Take a request_and_response object from pyswagger.App and check auth, token, headers, prepare the actual request and fill the response Note on performance : if you need more performance (because you are using this in a batch) you'd rather set raw_body_only=True, as parsed body is really slow. You'll then have to get data from response.raw and convert it to json using "json.loads(response.raw)" :param req_and_resp: the request and response object from pyswagger.App :param raw_body_only: define if we want the body to be parsed as object instead of staying a raw dict. [Default: False] :param opt: options, see pyswagger/blob/master/pyswagger/io.py#L144 :return: the final response. """ if opt is None: opt = {} try: # required because of inheritance request, response = super(EsiClient, self).request(req_and_resp, opt) except APIException as e: logger.info("Failed to execute request", e) ermsg = "No Message" if 'error' in e.response: ermsg = e.response['error'] elif 'message' in e.response: ermsg = e.response['message'] if e.status_code == 400 and ermsg == "invalid_token": # since the token is invalid lets delete it db.session.remove(current_user.ssoToken) db.session.commit() # fake a response that has the fields as expected return DummyResp(e.response, e.status_code) # check cache here so we have all headers, formed url and params cache_key = self.__make_cache_key(request) cached_response = self.cache.get(cache_key, None) if request.method == "GET" and cached_response is not None: res = cached_response else: # apply request-related options before preparation. request.prepare(scheme=self.prepare_schemes(request).pop(), handle_files=False) request._patch(opt) # prepare the request and make it. prepared_request = self._session.prepare_request( Request(method=request.method.upper(), url=request.url, params=request.query, data=request.data, headers=request.header)) start_api_call = time.time() res = self._session.send(prepared_request, stream=True, timeout=self.timeout) # event for api call stats API_CALL_STATS.send( url=res.url, status_code=res.status_code, elapsed_time=time.time() - start_api_call, message=res.content if res.status_code != 200 else None) if request.method == "GET" and res.status_code == 200: self.__cache_response(cache_key, res) if raw_body_only is None: response.raw_body_only = self.raw_body_only else: response.raw_body_only = raw_body_only response.apply_with(status=res.status_code, header=res.headers, raw=six.BytesIO(res.content).getvalue()) if 'warning' in res.headers: # send in logger and warnings, so the user doesn't have to use # logging to see it (at least once) logger.warning("[%s] %s" % (res.url, res.headers['warning'])) warnings.warn("[%s] %s" % (res.url, res.headers['warning'])) return response def __cache_response(self, cache_key, res): if 'expires' in res.headers: # this date is ALWAYS in UTC (RFC 7231) epoch = datetime(1970, 1, 1) expire = (datetime(*parsedate(res.headers['expires'])[:6]) - epoch).total_seconds() now = (datetime.utcnow() - epoch).total_seconds() cache_timeout = int(expire) - int(now) else: # if no expire, define that there is no cache # -1 will be now -1sec, so it'll expire cache_timeout = -1 # create a named tuple to store the data CachedResponse = namedtuple('CachedResponse', ['status_code', 'headers', 'content']) self.cache.set( cache_key, CachedResponse( status_code=res.status_code, headers=res.headers, content=res.content, ), cache_timeout, ) @classmethod def __make_cache_key(cls, request): headers = frozenset(request._p['header'].items()) path = frozenset(request._p['path'].items()) query = frozenset(request._p['query']) body = frozenset(request._p['body']) method = request.method return request.url, method, headers, path, query, body