"""
Biothings Web Handlers
biothings.web.handlers.BaseHandler
Supports:
- access to biothings.web.settings instance
- access to biothings.web.handlers logging stream
- access to default web templates
- Sentry error monitoring
Subclasses:
- biothings.web.handlers.StatusHandler
- biothings.web.handlers.FrontPageHandler
- discovery.web.handlers.BaseHandler
...
Also available:
biothings.web.handlers.BaseAPIHandler
biothings.web.handlers.BaseESRequestHandler
biothings.web.handlers.ESRequestHandler
"""
import logging
from elasticsearch.exceptions import ElasticsearchException
from tornado.web import RequestHandler
try:
from raven.contrib.tornado import SentryMixin
except ImportError:
class SentryMixin(object):
"""dummy class mixin"""
[docs]class BaseHandler(SentryMixin, RequestHandler):
"""
Parent class of all handlers, only direct descendant of `tornado.web.RequestHandler
<http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler>`_,
"""
@property
def web_settings(self):
try:
setting = self.settings['biothings']
except KeyError:
self.require_setting('biothings')
else:
return setting
[docs] def get_sentry_client(self):
"""
Override default and retrieve from tornado setting instead.
"""
client = self.settings.get('sentry_client')
if not client:
self.require_setting('sentry_client')
return client
@property
def logger(self):
return logging.getLogger("biothings.web.handlers")
[docs] def log_exception(self, *args, **kwargs):
"""
Only attempt to report to Sentry when the client is setup.
Discard when API key is not set or raven is not installed.
"""
if 'sentry_client' in self.settings:
return SentryMixin.log_exception(self, *args, **kwargs)
return RequestHandler.log_exception(self, *args, **kwargs)
[docs] def get_template_path(self):
if "template_path" in self.settings:
return super().get_template_path()
import biothings.web.templates
return next(iter(biothings.web.templates.__path__))
[docs] def data_received(self, chunk):
"""
Implement this method to handle streamed request data.
"""
# this dummy implementation silences the pylint abstract-class error
[docs]class StatusHandler(BaseHandler):
'''
Handles requests to check the status of the server.
Use set_status instead of raising exception so that
no error will be propogated to sentry monitoring.
'''
def head(self):
return self._check()
async def get(self):
dev = self.get_argument('dev', None)
res = await self._check(dev is not None)
self.finish(res)
async def _check(self, dev=False):
client = self.web_settings.connections.async_client
payload = self.web_settings.STATUS_CHECK
status = None # green, red, yellow
res = None # additional doc check
try:
health = await client.cluster.health()
status = health['status']
if payload:
res = await client.get(**payload)
except ElasticsearchException:
self.set_status(503)
else:
if status == 'red':
self.set_status(503)
if payload and not res:
self.set_status(503)
ret = {
"code": self.get_status(),
"status": status,
}
if dev:
ret.update({
"payload": payload,
"response": res
})
return ret
[docs]class FrontPageHandler(BaseHandler):
def get(self):
self.render(
template_name="home.html",
alert='Front Page Not Configured.',
title='Biothings API',
contents=self.web_settings.handlers.keys(),
support=self.web_settings.BIOTHING_TYPES,
url='http://biothings.io/'
)