def _factory(options): multipart_handler = media.MultipartFormHandler() for key, value in options.items(): setattr(multipart_handler.parse_options, key, value) req_handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_MULTIPART: multipart_handler, }) app = create_app(asgi) app.req_options.media_handlers = req_handlers app.resp_options.media_handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_MSGPACK: media.MessagePackHandler(), }) resource = AsyncMultipartAnalyzer() if asgi else MultipartAnalyzer() app.add_route('/submit', resource) app.add_route('/media', resource, suffix='media') app.add_route('/mirror', resource, suffix='mirror') return testing.TestClient(app)
def test_sync_methods_not_overridden(asgi): app = create_app(asgi) class FaultyHandler(media.BaseHandler): pass handlers = media.Handlers({'application/json': FaultyHandler()}) app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class Resource: def on_get(self, req, resp): resp.media = {} def on_post(self, req, resp): req.media class ResourceAsync: async def on_get(self, req, resp): resp.media = {} async def on_post(self, req, resp): await req.get_media() app.add_route('/', ResourceAsync() if asgi else Resource()) # NOTE(caselit): force serialization in xml, since error.to_json uses the faulty handler result = testing.simulate_get(app, '/', headers={'Accept': 'text/xml'}) assert result.status_code == 500 result = testing.simulate_post(app, '/', json={}, headers={'Accept': 'text/xml'}) assert result.status_code == 500
def test_sync_methods_not_overridden(asgi): app = create_app(asgi) class FaultyHandler(media.BaseHandler): pass handlers = media.Handlers({'application/json': FaultyHandler()}) app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class Resource: def on_get(self, req, resp): resp.media = {} def on_post(self, req, resp): req.media class ResourceAsync: async def on_get(self, req, resp): resp.media = {} async def on_post(self, req, resp): await req.get_media() app.add_route('/', ResourceAsync() if asgi else Resource()) result = testing.simulate_get(app, '/') assert result.status_code == 500 result = testing.simulate_post(app, '/', json={}) assert result.status_code == 500
def get_app(): app = falcon.API( media_type='application/json', middleware=[ AuthMiddleWare(), ], ) #have our custom json handler here handlers = media.Handlers({ 'application/json': CustomJSONHandler(), }) #register the custom handler here app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers #add custom error handler app.add_error_handler(falcon.HTTPError, handler=api_error_handler) #add routes for up in URL_PATTERNS: for i in up: app.add_route(i[0], i[1]) #print("Link URL ",i[0],) return app
def test_json_err_no_handler(asgi, monkeypatch_resolver): app = create_app(asgi) handlers = media.Handlers( {falcon.MEDIA_URLENCODED: media.URLEncodedFormHandler()}) # NOTE(kgriffs): Test the pre-3.0 method. Although undocumented, it was # technically a public method, and so we make sure it still works here. if monkeypatch_resolver: def _resolve(media_type, default, raise_not_found=True): with pytest.warns(DeprecatedWarning, match='This undocumented method'): h = handlers.find_by_media_type( media_type, default, raise_not_found=raise_not_found) return h, None, None handlers._resolve = _resolve app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class Resource: def on_get(self, req, resp): raise falcon.HTTPForbidden() app.add_route('/', Resource()) result = testing.simulate_get(app, '/') assert result.status_code == 403 assert result.json == falcon.HTTPForbidden().to_dict()
def __init__(self): handlers = media.Handlers({ 'application/json': media.JSONHandler() }) self.db_middleware = DatabaseMiddleware() self.config_middleware = ConfigMiddleware() self.validation_middleware = ValidationMiddleware() self.negotiation_middleware = NegotiationMiddleware() self.user_middleware = UserMiddleware() self.api = falcon.API(middleware=[ self.config_middleware, self.db_middleware, self.user_middleware, self.validation_middleware, self.negotiation_middleware ]) self.api.req_options.media_handlers = handlers self.api.req_options.auto_parse_form_urlencoded = True self.api.resp_options.media_handlers = handlers self.api.add_error_handler(Exception, error_handler) self.api.resp_options.secure_cookies_by_default = False # Routes dir_path = os.path.dirname(os.path.realpath(__file__)) # Home route self.api.add_route('/app/home', HomeResource()) # User routes self.api.add_route('/app/create', UserCreateResource()) self.api.add_route('/app/create_email_exists', UserCreateEmailExistsResource()) self.api.add_route('/app/login', UserLoginResource()) self.api.add_route('/app/login_failed', UserLoginFailedResource()) self.api.add_route('/app/session_expired', SessionExpiredResource()) self.api.add_route('/app/logout', UserLogoutResource()) self.api.add_route('/api/users', UserCollectionResource()) # Session routes self.api.add_route('/api/session', SessionResource()) # Pomodoro routes self.api.add_route('/app/pomodoro', PomodoroResource()) self.api.add_route('/app/pomodoro_set', PomodoroSetResource()) self.api.add_route('/app/pom_exists', PomodoroExistsErrorResource()) self.api.add_route('/app/pom_invalid', PomodoroValidationErrorResource()) self.api.add_route('/api/pom_validation', PomodoroValidationResource()) self.api.add_route('/api/poms/today', PomodoroCollectionTodayResource()) self.api.add_route('/api/poms', PomodoroCollectionResource()) self.api.add_route('/api/delete_poms', DeletePomsResource()) self.api.add_route('/api/flag_types', FlagTypesResource()) self.api.add_route('/api/pom_sheet_export', PomSheetExportResource()) self.api.add_route('/app/export_poms', ExportPomsResource()) # Settings route self.api.add_route('/app/settings', UserSettingsResource()) # Static directory routes self.api.add_static_route('/css', dir_path + '/css') self.api.add_static_route('/js', dir_path + '/js') self.api.add_static_route('/assets', dir_path + '/assets')
def test_deserialization_raises(asgi): app = create_app(asgi) class SuchException(Exception): pass class FaultyHandler(media.BaseHandler): def deserialize(self, stream, content_type, content_length): raise SuchException('Wow such error.') def deserialize_async(self, stream, content_type, content_length): raise SuchException('Wow such error.') def serialize(self, media, content_type): raise SuchException('Wow such error.') handlers = media.Handlers({'application/json': FaultyHandler()}) app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class Resource: def on_get(self, req, resp): resp.media = {} def on_post(self, req, resp): req.media class ResourceAsync: async def on_get(self, req, resp): resp.media = {} async def on_post(self, req, resp): # NOTE(kgriffs): In this one case we use the property # instead of get_media(), in order to test that the # alias works as expected. await req.media app.add_route('/', ResourceAsync() if asgi else Resource()) # NOTE(kgriffs): Now that we install a default handler for # Exception, we have to clear them to test the path we want # to trigger. # TODO(kgriffs): Since we always add a default error handler # for Exception, should we take out the checks in the WSGI/ASGI # callable and just always assume it will be handled? If so, # it makes testing that the right exception is propagated harder; # I suppose we'll have to look at what is logged. app._error_handlers.clear() with pytest.raises(SuchException): testing.simulate_get(app, '/') with pytest.raises(SuchException): testing.simulate_post(app, '/', json={})
def _factory(options): multipart_handler = media.MultipartFormHandler() for key, value in options.items(): setattr(multipart_handler.parse_options, key, value) handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_MULTIPART: multipart_handler, }) app = falcon.App() app.req_options.media_handlers = handlers app.add_route('/media', MultipartAnalyzer()) return testing.TestClient(app)
def client(): handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_URLENCODED: media.URLEncodedFormHandler(), }) api = falcon.API() api.req_options.media_handlers = handlers api.resp_options.media_handlers = handlers api.add_route('/media', MediaMirror()) return testing.TestClient(api)
def test_json_err_no_handler(asgi): app = create_app(asgi) handlers = media.Handlers({falcon.MEDIA_URLENCODED: media.URLEncodedFormHandler()}) app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class Resource: def on_get(self, req, resp): raise falcon.HTTPForbidden() app.add_route('/', Resource()) result = testing.simulate_get(app, '/') assert result.status_code == 403 assert result.json == falcon.HTTPForbidden().to_dict()
def test_deserialize_custom_media(custom_client): class FirstByteHandler(media.BaseHandler): exhaust_stream = True def deserialize(self, stream, content_type, content_length): first_byte = stream.read(1) if first_byte: return '0x{:02x}'.format(first_byte[0]) return '' async def deserialize_async(self, stream, content_type, content_length): first_byte = await stream.read(1) if first_byte: return '0x{:02x}'.format(first_byte[0]) return '' handlers = media.Handlers( {'application/x-falcon-first-byte': FirstByteHandler()}) client = custom_client({'media_handlers': handlers}) data = (b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="first"\r\n' b'Content-Type: application/x-falcon-first-byte\r\n\r\n' b'\r\n' b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="second"\r\n' b'Content-Type: application/x-falcon-first-byte\r\n\r\n' b'Hi!\r\n' b'--BOUNDARY--\r\n') resp = client.simulate_post( '/media', headers={'Content-Type': 'multipart/form-data; boundary=BOUNDARY'}, body=data, ) assert resp.status_code == 200 assert resp.json == ['', '0x48']
def test_async_handler_returning_none(): app = create_app(asgi=True) class SimpleHandler(media.BaseHandler): def serialize(self, media, content_type): return json.dumps(media).encode() def deserialize(self, stream, content_type, content_length): return None handlers = media.Handlers({'application/json': SimpleHandler()}) app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class ResourceAsync: async def on_post(self, req, resp): resp.media = [await req.get_media()] app.add_route('/', ResourceAsync()) doc = {'event': 'serialized'} result = testing.simulate_post(app, '/', json=doc) assert result.status_code == 200 assert result.json == [None]
import falcon from falcon import media from middleware import SqlAlchemySessionManager, SqlAlchemyMediaHandler, Session import resources handlers = media.Handlers({ 'application/json': SqlAlchemyMediaHandler, }) app = falcon.API(middleware=[SqlAlchemySessionManager(Session)]) app.resp_options.media_handlers = handlers app.add_route('/cats', resources.CatsRessource()) app.add_route('/cats/{id}', resources.CatRessource())
from look.api import auth, db, gql from look.middleware.requestdatamanager import RequetDataManager from look.middleware.dbmanager import DBManager from look.middleware.socketmanager import SocketManager from look.schema import schema from falcon import media from falcon.media import MultipartFormHandler from falcon.media.multipart import MultipartParseOptions db_session, engine = init_db() parse_options = MultipartParseOptions() parse_options.max_body_part_buffer_size = 3 * 1024 * 1024 handlers = media.Handlers({ 'multipart/form-data': MultipartFormHandler(parse_options=parse_options), }) middleware = [ RequetDataManager(), DBManager(db_session, schema), ] app = application = falcon.asgi.App(middleware=middleware) app.req_options.strip_url_path_trailing_slash = True app.req_options.media_handlers.update(handlers) app.resp_options.media_handlers.update(handlers) class RootPage(object):
Delivers data for a particular luftdaten.info sensor """ url = "http://api.luftdaten.info/v1/sensor/%s/" % sensor_id r = requests.get(url) if r.status_code == 200: maxage = 60 * 5 # 5 minutes resp.cache_control = ["max_age=%d" % maxage] resp.media = r.json() else: resp.media = r.text resp.status = str(r.status_code) + " Unknown Error" handlers = media.Handlers({ 'application/json': jsonhandler.JSONHandler(), }) cors = CORS(allow_all_origins=True, allow_all_headers=True) app = falcon.API(middleware=[cors.middleware]) app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers app.add_route('/events/', EventsResource()) app.add_route('/feed/', FeedResource()) app.add_route('/luftdaten.info/v1/sensor/{sensor_id}/', ParticleSensorResource()) app.add_route('/', IndexResource())
def test_deserialization_raises(asgi, handler_mt, monkeypatch_resolver): app = create_app(asgi) class SuchException(Exception): pass class FaultyHandler(media.BaseHandler): def deserialize(self, stream, content_type, content_length): raise SuchException('Wow such error.') def deserialize_async(self, stream, content_type, content_length): raise SuchException('Wow such error.') def serialize(self, media, content_type): raise SuchException('Wow such error.') handlers = media.Handlers({handler_mt: FaultyHandler()}) # NOTE(kgriffs): Test the pre-3.0 method. Although undocumented, it was # technically a public method, and so we make sure it still works here. if monkeypatch_resolver: def _resolve(media_type, default, raise_not_found=True): with pytest.warns(DeprecatedWarning, match='This undocumented method'): h = handlers.find_by_media_type( media_type, default, raise_not_found=raise_not_found) return h, None, None handlers._resolve = _resolve app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers class Resource: def on_get(self, req, resp): resp.media = {} def on_post(self, req, resp): req.media class ResourceAsync: async def on_get(self, req, resp): resp.media = {} async def on_post(self, req, resp): # NOTE(kgriffs): In this one case we use the property # instead of get_media(), in order to test that the # alias works as expected. await req.media app.add_route('/', ResourceAsync() if asgi else Resource()) # NOTE(kgriffs): Now that we install a default handler for # Exception, we have to clear them to test the path we want # to trigger. # TODO(kgriffs): Since we always add a default error handler # for Exception, should we take out the checks in the WSGI/ASGI # callable and just always assume it will be handled? If so, # it makes testing that the right exception is propagated harder; # I suppose we'll have to look at what is logged. app._error_handlers.clear() with pytest.raises(SuchException): testing.simulate_get(app, '/') with pytest.raises(SuchException): testing.simulate_post(app, '/', json={})
def __init__(self, path, resource_resolver, **kwargs): spec = Object.from_file(OpenApi, path) middleware = list(kwargs.pop('middleware', [])) middleware.append(ValidationMiddleware()) request_type = kwargs.pop('request_type', None) assert request_type is None or issubclass( request_type, FalconHeavyRequest), ( "Request type MUST be subclass of `FalconHeavyRequest`") request_type = request_type or FalconHeavyRequest response_type = kwargs.pop('response_type', None) assert response_type is None or issubclass( response_type, FalconHeavyResponse), ( "Response type MUST be subclass of `FalconHeavyResponse`") response_type = response_type or FalconHeavyResponse super(FalconHeavyApi, self).__init__(middleware=middleware, request_type=request_type, response_type=response_type, **kwargs) self.req_options = FalconHeavyRequestOptions() self.req_options.keep_blank_qs_values = True self.req_options.auto_parse_qs_csv = False self.req_options.auto_parse_form_urlencoded = False self.add_error_handler(FalconHeavyHTTPError) # Replace standard media handlers in the responses to serialize # the date and date-time objects according to the specification self.resp_options.media_handlers = media.Handlers({ 'application/json': FalconHeavyJSONHandler(), 'application/json; charset=UTF-8': FalconHeavyJSONHandler(), }) for uri_template, path_item in iteritems(spec['paths']): resource_id = path_item['x-resource'] lazy = resource_resolver.resolve(resource_id) if lazy is None: logger.error("Resource `%s` is not resolved. Skip path `%s`", resource_id, uri_template) continue assert callable( lazy), "Resource `{}` MUST be resolved as callable".format( resource_id) operation_schemas = {} for http_method in falcon.HTTP_METHODS: operation = path_item.get(http_method.lower()) if operation is None: continue operation_schema = operation_schemas.setdefault( http_method, {}) parameters = operation.get('parameters') if parameters is not None: operation_schema[ 'parameters'] = ParametersFactory.generate(parameters) request_body = operation.get('requestBody') if request_body is not None: operation_schema[ 'requestBody'] = RequestBodyFactory.generate( request_body) operation_schema['responses'] = ResponsesFactory.generate( operation['responses']) operation_schema['security'] = operation.get('security') resource = lazy() assert resource, "Resource `{}` is not instantiated".format( resource_id) setattr(resource, '__operation_schemas', operation_schemas) self.add_route(uri_template, resource)
def create_app(infer, metric_registry, health_check, req_schema, resp_schema, use_msgpack, config): """ create :class:`falcon` application :param infer: model infer function (contains `preprocess`, `inference`, and `postprocess`) :param metric_registry: Prometheus metric registry :param health_check: model health check function (need examples provided in schema) :param req_schema: request schema defined with :class:`pydantic.BaseModel` :param resp_schema: request schema defined with :class:`pydantic.BaseModel` :param bool use_msgpack: use msgpack for serialization or not (default: JSON) :param config: configs :class:`ventu.config.Config` :return: a :class:`falcon` application """ if use_msgpack: handlers = media.Handlers({ 'application/msgpack': media.MessagePackHandler(), }) app = API(media_type='application/msgpack') app.req_options.media_handlers = handlers app.resp_options.media_handlers = handlers else: app = API() api = SpecTree('falcon', title=config.name, version=config.version) logger = logging.getLogger(__name__) VALIDATION_ERROR_COUNTER = Counter( 'validation_error_counter', 'numbers of validation errors', registry=metric_registry, ) def counter_hook(req, resp, resource): if resp.status == HTTP_422: VALIDATION_ERROR_COUNTER.inc() class Homepage: def on_get(self, req, resp): logger.debug('return service endpoints') resp.media = { 'health check': { '/health': 'GET' }, 'metrics': { '/metrics': 'GET' }, 'inference': { '/inference': 'POST' }, 'API document': { '/apidoc/redoc': 'GET', '/apidoc/swagger': 'GET' } } class Metrics: def __init__(self): if os.environ.get('prometheus_multiproc_dir'): multiprocess.MultiProcessCollector(metric_registry) def on_get(self, req, resp): resp.content_type = CONTENT_TYPE_LATEST resp.body = generate_latest(metric_registry) class HealthCheck: @api.validate(resp=Response(HTTP_200=ServiceStatus)) def on_get(self, req, resp): """ Health check """ status = ServiceStatus(inference=StatusEnum.ok) try: health_check() except AssertionError as err: status.inference = StatusEnum.error logger.warning(f'Service health check error: {err}') logger.debug(str(status)) resp.media = status.dict() class Inference: @after(counter_hook) @api.validate(json=req_schema, resp=Response(HTTP_200=resp_schema)) def on_post(self, req, resp): """ Deep learning model inference """ data = req.context.json logger.debug(str(data)) resp.media = infer(data) app.add_route('/', Homepage()) app.add_route('/health', HealthCheck()) app.add_route('/metrics', Metrics()) app.add_route('/inference', Inference()) api.register(app) return app