def test_get_remote_addr(): """ Test using the get_remote_addr() key function as default """ limiter = Limiter( key_func=get_remote_addr, default_limits=["10 per hour", "1 per second"] ) @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 # due to the 1 second default limit on the default limiter r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200
def make_api(self): self.before_hooks.extend(self.hooks.optional_request_hooks()) self.after_hooks.extend(self.hooks.optional_response_hooks()) api = API(before=self.before_hooks, after=self.after_hooks) # Set the default route to the NYI object api.add_sink(self.default_route or NYI(before=self.before_hooks, after=self.after_hooks)) # Add Error Handlers - ordered generic to more specific built_in_handlers = [(Exception, handle_unexpected_errors), (ResponseException, ResponseException.handle), (InvalidTokenError, InvalidTokenError.handle)] for ex, handler in built_in_handlers + self._error_handlers: api.add_error_handler(ex, wrap_handler_with_hooks(handler, self.after_hooks)) # Add all the routes collected thus far for _, disp in self._dispatchers.items(): for endpoint, handler in disp.get_routes(): LOG.debug("Loading endpoint %s", endpoint) api.add_route(endpoint, handler) api.add_route('%s.json' % endpoint, handler) return api
def test_limit_by_resource_and_method(): """ Test using a custom key_func - one which creates different buckets by resource and method """ def get_key(req, resp, resource, params) -> str: user_key = get_remote_addr(req, resp, resource, params) return f"{user_key}:{resource.__class__.__name__}:{req.method}" limiter = Limiter( key_func=get_key, default_limits=["10 per hour", "1 per second"] ) @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' def on_post(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_429 # but a different endpoint can still be hit r = client.simulate_post('/things') assert r.status == HTTP_200
def test_limit_on_method_overwrite(limiter): """ Test the limit decorator on the method overwriting the default limit """ class ThingsResource: # the default limit on 'limiter' is 1 per second @limiter.limit(limits="2 per second") def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200
def test_caching_content_type(caches, eviction_strategy): """ Testing that the Content-Type header gets cached even when it is not the default, but it is set in the responder """ # get the cache for the given eviction strategy cache = caches[eviction_strategy] # a resource where a custom response Cache-Type is set class CachedResource: @cache.cached(timeout=1) def on_get(self, req, resp): resp.content_type = 'mycustom/verycustom' if FALCONVERSION_MAIN < 3: resp.body = json.dumps({'num': random.randrange(0, 100000)}) else: resp.text = json.dumps({'num': random.randrange(0, 100000)}) app = API(middleware=cache.middleware) app.add_route('/randrange_cached', CachedResource()) client = testing.TestClient(app) # the first call will cache it result1 = client.simulate_get('/randrange_cached') assert result1.headers['Content-Type'] == 'mycustom/verycustom' # the second call returns it from cache - but it still carries # the same content-type result2 = client.simulate_get('/randrange_cached') assert result1.json['num'] == result2.json['num'] assert result2.headers['Content-Type'] == 'mycustom/verycustom'
def test_no_limit(limiter): """ Test a no limit resource even when another resource has a limit """ @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' class ThingsResourceNoLimit: def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) app.add_route('/thingsnolimit', ThingsResourceNoLimit()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 # due to the 1 second default limit on the default limiter r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200 for i in range(3): r = client.simulate_get('/thingsnolimit') assert r.status == HTTP_200
def test_default_limit(limiter): """ Test the default limit applied through the class decorator """ @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' def some_other_method(self): pass app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 # due to the 1 second default limit on the default limiter r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200
def test_redis(redis_server): """ Test using the redis backend """ limiter = Limiter( key_func=get_remote_addr, default_limits=["10 per hour", "1 per second"], config={ 'RATELIMIT_KEY_PREFIX': 'myapp', 'RATELIMIT_STORAGE_URL': f'redis://@localhost:{REDIS_PORT}' } ) @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 # due to the 1 second default limit on the default limiter r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200
def test_limit_class_and_method(limiter): """ Class level decorator gets overwritten by the method level one """ # the default limit on 'limiter' is 1 per second @limiter.limit(limits="5 per hour;2 per second") class ThingsResource: # also the limits are in an unusual order: @limiter.limit(limits="3 per second;5 per hour") def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200
def test_limit_w_semicol(limiter): """ The limit might be provided semicol-separated """ class ThingsResource: # the default limit on 'limiter' is 1 per second @limiter.limit(limits="5 per hour;2 per second") def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things') assert r.status == HTTP_200
def hydrated_client_multiple_middleware(): """A Falcon API with an endpoint for testing Marshmallow, with all included middlewares registered.""" data_store = DataStore() class PhilosopherResource: schema = Philosopher() def on_get(self, req, resp, phil_id): """Get a philosopher""" req.context['result'] = data_store.get(phil_id) class PhilosopherCollection: schema = Philosopher() def on_post(self, req, resp): req.context['result'] = data_store.insert(req.context['json']) app = API(middleware=[ m.Marshmallow(), m.JSONEnforcer(), m.EmptyRequestDropper()]) app.add_route('/philosophers', PhilosopherCollection()) app.add_route('/philosophers/{phil_id}', PhilosopherResource()) yield testing.TestClient(app) data_store.clear()
def get_falcon_api( broker_resource: BrokerResource, settings: Settings = Settings(), use_fallback_sessions: bool = False, ) -> API: logging.basicConfig(level=settings.logging.level.value) beaker_settings = { "session.type": settings.beaker.type, "session.cookie_expires": True, "session.auto": True, # We got to have this on as things are currently programmed. "session.key": "JSESSIONID", "session.secure": True, "session.httponly": True, "session.data_dir": settings.beaker.data_dir, } beaker_middleware = None if not use_fallback_sessions: beaker_middleware = BeakerSessionMiddleware(beaker_settings) else: beaker_middleware = FallbackSessionMiddleware(beaker_settings) api = API(middleware=beaker_middleware, response_type=CookieCaseFixedResponse) api.add_route("/pcoip-broker/xml", resource=broker_resource) return api
def app(request, limiter): """ Creates a Falcon app with the default limiter """ @limiter.limit() class ThingsResource: # unmarked methods will use the default limit def on_get(self, req, resp): resp.body = 'Hello world!' # mark this method with a special limit # which will overwrite the default @limiter.limit(limits="1 per day") def on_post(self, req, resp): pass # a resource with no limits: class ThingsResourceNoLimit: # unmarked methods will use the default limit def on_get(self, req, resp): pass # add the limiter middleware to the Falcon app app = API(middleware=limiter.middleware) things = ThingsResource() thingsnolimit = ThingsResourceNoLimit() app.add_route('/things', things) app.add_route('/thingsnolimit', thingsnolimit) return app
def add_routes(api: falcon.API, cfg: Config): """ Registers endpoints with Falcon Args: - api: Falcon API object - cfg: Configuration """ api.add_route(HealthCheckResource.Path, HealthCheckResource())
def hydrated_client_multiple_middleware(): """Create a server for testing, with all included middlewares""" data_store = DataStore() class PhilosopherResource: schema = Philosopher() def on_get(self, req, resp, phil_id): """Get a philosopher""" req.context["result"] = data_store.get(phil_id) class PhilosopherCollection: schema = Philosopher() def on_post(self, req, resp): req.context["result"] = data_store.insert(req.context["json"]) app = API(middleware=[ m.Marshmallow(), m.JSONEnforcer(), m.EmptyRequestDropper() ]) app.add_route("/philosophers", PhilosopherCollection()) app.add_route("/philosophers/{phil_id}", PhilosopherResource()) yield testing.TestClient(app) data_store.clear()
class Server(BaseApplication): resources = { '/files': FilesResource(), '/file/{file_id}': FileResource(), '/file/{file_id}/mirrors': FileMirrorsResource(), '/file/{file_id}/mirror/{mirror_id}': FileMirrorResource(), '/file/{file_id}/mirror/{mirror_id}/reports': FileMirrorReportsResource() } def __init__(self, options=None): self.falcon = API() self.options = options or {} self.register_resources() super(Server, self).__init__() def load_config(self): config = { 'bind': '{}:{}'.format( self.options.get('bind') or '127.0.0.1', self.options.get('port') or 8000) } for key, value in config.items(): self.cfg.set(key, value) def load(self): return self.falcon def register_resources(self): for route, resource in self.resources.items(): self.falcon.add_route(route, resource)
def hydrated_client(): """Create a Falcon API with an endpoint for testing Marshmallow""" data_store = DataStore() class PhilosopherResource: schema = Philosopher() def on_get(self, req, resp, phil_id): """Get a philosopher""" req.context["result"] = data_store.get(phil_id) class PhilosopherCollection: schema = Philosopher() def on_post(self, req, resp): req.context["result"] = data_store.insert(req.context["json"]) app = API(middleware=[m.Marshmallow()]) app.add_route("/philosophers", PhilosopherCollection()) app.add_route("/philosophers/{phil_id}", PhilosopherResource()) yield testing.TestClient(app) data_store.clear()
def test_dynamic_limits_on_method2(): """ Test using the dynamic_limits param of the method decorators to change the limit per user Overwriting the default limits from the class level decortor """ limiter = Limiter(key_func=get_remote_addr, default_limits=["10 per hour", "1 per second"]) @limiter.limit() class ThingsResource: @limiter.limit(dynamic_limits=lambda req, resp, resource, req_succeeded: '5/second' if req.get_header('APIUSER') == 'admin' else '2/second') def on_get(self, req, resp): resp.body = 'Hello world!' def on_post(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) #### # 'normal' user - errors after more than 2 calls per sec r = client.simulate_get('/things') assert r.status == HTTP_200 r = client.simulate_get('/things') assert r.status == HTTP_200 # due to the 2/second limit r = client.simulate_get('/things') assert r.status == HTTP_429 ######### # 'admin' user should be able to make 5 calls admin_header = {"APIUSER": "******"} for i in range(5): r = client.simulate_get('/things', headers=admin_header) assert r.status == HTTP_200 # at the 6th hit even the admin user will error: r = client.simulate_get('/things', headers=admin_header) assert r.status == HTTP_429 sleep(1) r = client.simulate_get('/things', headers=admin_header) assert r.status == HTTP_200 ######## # the on_post() method gets the default limit - 1 per second r = client.simulate_post('/things', headers=admin_header) assert r.status == HTTP_200 r = client.simulate_post('/things', headers=admin_header) assert r.status == HTTP_429
def test_caching_multiple_decorators_on_method(caches, eviction_strategy): """ Testing caching when there are multiple decorators on the method """ # get the cache for the given eviction strategy cache = caches[eviction_strategy] class CachedResource: # a resource where the cache is the first decorator @cache.cached(timeout=1) @a_decorator def on_get(self, req, resp): if FALCONVERSION_MAIN < 3: resp.body = json.dumps({'num': random.randrange(0, 100000)}) else: resp.text = json.dumps({'num': random.randrange(0, 100000)}) class CachedResource2: # a resource where the cache is NOT the first decorator @a_decorator @cache.cached(timeout=1) def on_get(self, req, resp): if FALCONVERSION_MAIN < 3: resp.body = json.dumps({'num': random.randrange(0, 100000)}) else: resp.text = json.dumps({'num': random.randrange(0, 100000)}) class CachedResource3: # a resource where the cache is NOT the first decorator, but the register() is used @register(a_decorator, cache.cached(timeout=1)) def on_get(self, req, resp): if FALCONVERSION_MAIN < 3: resp.body = json.dumps({'num': random.randrange(0, 100000)}) else: resp.text = json.dumps({'num': random.randrange(0, 100000)}) app = API(middleware=cache.middleware) app.add_route('/randrange_cached', CachedResource()) app.add_route('/randrange_cached2', CachedResource2()) app.add_route('/randrange_cached3', CachedResource3()) client = testing.TestClient(app) # scenario 1 - the cache is the first decorator result1 = client.simulate_get('/randrange_cached') result2 = client.simulate_get('/randrange_cached') assert result1.json['num'] == result2.json['num'] # scenario 2 - the cache is NOT the first decorator - caching does NOT work! result1 = client.simulate_get('/randrange_cached2') result2 = client.simulate_get('/randrange_cached2') assert result1.json['num'] != result2.json['num'] # scenario 3 - the cache is the NOT first decorator, but register() is used, so caching does work! result1 = client.simulate_get('/randrange_cached3') result2 = client.simulate_get('/randrange_cached3') assert result1.json['num'] == result2.json['num']
def add_apis(app: falcon.API) -> None: for file in _CURRENT_DIR.glob('*.py'): if file.name.startswith('_'): continue importlib.import_module(f'.{file.stem}', 'regex') for klass in _BaseAPI.__subclasses__(): uri_template = klass.__name__.lower().rstrip('api') app.add_route(f'/{uri_template}', klass())
def test_different_key_prefixes(): """ Test using different key_prefixes """ limiter1 = Limiter( key_func=get_remote_addr, default_limits=["10 per hour", "1 per second"], config={ 'RATELIMIT_KEY_PREFIX': 'app1' } ) @limiter1.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' app1 = API(middleware=limiter1.middleware) app1.add_route('/things', ThingsResource()) client1 = testing.TestClient(app1) r = client1.simulate_get('/things') assert r.status == HTTP_200 # due to the 1 second default limit on the default limiter r = client1.simulate_get('/things') assert r.status == HTTP_429 ##### # app2 - with a different key limiter2 = Limiter( key_func=get_remote_addr, default_limits=["10 per hour", "1 per second"], config={ 'RATELIMIT_KEY_PREFIX': 'app2' } ) @limiter2.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' app2 = API(middleware=limiter2.middleware) app2.add_route('/things', ThingsResource()) client2 = testing.TestClient(app2) r = client2.simulate_get('/things') assert r.status == HTTP_200 # due to the 1 second default limit on the default limiter r = client2.simulate_get('/things') assert r.status == HTTP_429
def create_api(model_container=None): api = API() if not model_container: # model_container instance model_container = ModelContainer() # handling requests api.add_route('/predict', PredictController(model_container)) api.add_route('/info', InfoController()) return api
def create_client(uri_template, resource, handlers=None): app = API() app.add_route(uri_template, resource) if handlers: app.req_options.media_handlers.update(handlers) client = testing.TestClient(app) client.resource = resource return client
def app(environment=None): """ Create our echo falcon app. """ if environment is None: environment = os.environ logging.basicConfig() logging.getLogger("falcon").setLevel(logging.DEBUG) api = API() api.add_route("/ping", PingResource()) api.add_sink(sink(environment)) return api
def create_client(resource=None, middleware=None, handlers=None): res = resource or SimpleTestResource() app = API(middleware=middleware) app.add_route('/', res) if handlers: app.req_options.media_handlers.update(handlers) client = TestClient(app) client.resource = res return client
def set_routes(api: API, db: Session) -> API: user = UserResource(db) api.add_route('/statz', Statz()) api.add_route('/users/{user_id}', user) api.add_route('/users', user) api.add_route('/login', LoginResource(db)) return api
def add_api_routes(app: falcon.API, runner: JobRunner): logger.info('Adding routes to api') app.add_route('/submit', SubmitJobResource(runner)) app.add_route('/status/{job_id}', JobStatusResource(runner)) app.add_route('/result/{job_id}', ClientCallbackResource(runner)) app.add_route('/test', TestingEndpoint()) logger.info('All routes added')
def main(): global app specfile = "./apis/api.yaml" specurl = "file://" + abspath(specfile) specdict = read_yaml_file(specfile) openapi_spec = create_spec(specdict, spec_url=specurl) openapi_middleware = FalconOpenAPIMiddleware.from_spec(openapi_spec) app = API(middleware=[openapi_middleware]) auth_server = Auth() user_server = User() app.add_route('/user', user_server) app.add_route('/token', auth_server)
def test_undefined_endpoint(limiter): """ Test calling a method which is not defined at all Our module should not error, but we should leave it to Falcon to handle it - and return a 405. """ @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' app = API(middleware=limiter.middleware) app.add_route('/things', ThingsResource()) client = testing.TestClient(app) r = client.simulate_post('/things') assert r.status == HTTP_405
def autowire_resources(api: falcon.API, spec: Specification, resources: list, ignore_errors: bool = False) -> None: """Resources must be annotated with @tag(path=) You can inspect each operation's ``handler`` attribute to find the routed function. .. code-block: python >>> @tag(path="/users{userId}/widgets") ... class WidgetCollectionResource: ... def on_get(self): ... """ def fail(msg: str) -> None: if ignore_errors: logger.warning(msg) else: raise RuntimeError(msg) for resource in resources: cls = resource.__class__ path = get_tag("path", resource) if not path: fail(f"{cls} does not have an @tag(path=) annotation") continue defined_operations = spec.operations.with_path(path) if not defined_operations: fail(f"{cls}@{path} has no known operations") has_operations = { o for o in defined_operations if hasattr(resource, f"on_{o.verb}") } # type: Set[Operation] missing_operations = [o for o in (defined_operations - has_operations)] if missing_operations: missing_operations.sort(key=lambda o: o.id) fail(f"{cls}@{path} is missing the following handlers:" + "".join(f"\n {o.id}::on_{o.verb}" for o in missing_operations)) api.add_route(path, resource) for o in has_operations: logger.info(f"added route for {o.id} {o.verb} {o.path}") o.handler = getattr(resource, f"on_{o.verb}") missing_operations = [o.id for o in spec.operations if not o.handler] if missing_operations: missing_operations.sort() fail("The following operations do not have handlers:" + "".join(f"\n {o}" for o in missing_operations))
def main(): if not docker_is_available(): print("Docker does not seem to be available..") return 1 docker_client = Docker() languages_handler = Languages(docker_client) code_exec_handler = CodeExec(docker_client) app = API() app.add_route("/exec/{lang}", code_exec_handler) app.add_route("/", languages_handler) print("Starting on {host}:{port}".format(host=application.host, port=application.port)) WSGIServer((application.host, application.port), app).serve_forever() return 0
def client(): api = API(middleware=ValidatorComponent()) api.add_route(SONGFINISH_ROUTE, SongFinish(token=TOKEN)) return testing.TestClient(api)
import sys import testtools try: from StringIO import StringIO except ImportError: from io import StringIO from falcon import API from falcon.cmd import print_routes _api = API() _api.add_route('/test', None) STDOUT = sys.stdout class TestPrintRoutes(testtools.TestCase): def setUp(self): """Capture stdout""" super(TestPrintRoutes, self).setUp() self.output = StringIO() sys.stdout = self.output def tearDown(self): """Reset stdout""" super(TestPrintRoutes, self).tearDown() self.output.close()
class AuthTestsMixin: """ Test mixin that defines common routine for testing auth classes. """ class SkipTest(Exception): """Raised when given tests is marked to be skipped Note: we use this exception instead of self.skipTest() method because this has slightly different semantics. We simply don't want to report these tests as skipped. """ route = '/foo/' user = { "username": "******", "details": "bar", "password": "******", "allowed_ip": "127.100.100.1", "allowed_remote": "127.0.0.1", "token": "s3cr3t70ken", 'allowed_ip_range': ['127.100.100.1'], } ident_keys = ['password'] auth_storage = ExampleKVUserStorage() auth_middleware = [authentication.Anonymous(user)] def get_authorized_headers(self): raise NotImplementedError def get_invalid_headers(self): raise NotImplementedError def get_unauthorized_headers(self): return {} def setUp(self): super().setUp() self.api = API(middleware=self.auth_middleware) self.api.add_route(self.route, ExampleResource()) self.auth_storage.clear() identity = [self.user[key] for key in self.ident_keys] self.auth_storage.register( self.auth_middleware[0], identity[0] if len(identity) == 1 else identity, self.user ) def test_unauthorized(self): try: self.simulate_request( self.route, decode='utf-8', method='GET', headers=self.get_unauthorized_headers() ) assert self.srmock.status == status_codes.HTTP_UNAUTHORIZED except self.SkipTest: pass def test_authorized(self): try: self.simulate_request( self.route, decode='utf-8', method='GET', headers=self.get_authorized_headers() ) assert self.srmock.status == status_codes.HTTP_OK except self.SkipTest: pass def test_bad_request(self): try: maybe_multiple_headers_sets = self.get_invalid_headers() if isinstance(maybe_multiple_headers_sets, tuple): header_sets = maybe_multiple_headers_sets else: header_sets = (maybe_multiple_headers_sets,) for headers in header_sets: self.simulate_request( self.route, decode='utf-8', method='GET', headers=headers ) assert self.srmock.status == status_codes.HTTP_BAD_REQUEST except self.SkipTest: pass
class FalconAdapter(Adapter): def __init__(self, *args, **kwargs): super(FalconAdapter, self).__init__(*args, **kwargs) self.falcon_api = FalconAPI() # Add Falcon-specific URI routes for uri, resource in self.get_embedded_rules(): self.falcon_api.add_route(uri, resource) def adapt_handler(self, handler, resource, request, response, *args, **kwargs): """Adapt the request object and the response object for the `handler` function. :param handler: the handler function to be adapted. :param resource: the Falcon resource object. :param request: the Falcon request object. :param response: the Falcon response object. :param args: a list of positional arguments that will be passed to the handler. :param kwargs: a dictionary of keyword arguments that will be passed to the handler. """ adapted_request = FalconRequest(request) api_response = handler(adapted_request, *args, **kwargs) # Convert the RESTArt response object to a Falcon response object response.body = api_response.data response.status = api_response.status response.set_headers(api_response.headers) def wsgi_app(self, environ, start_response): """The actual Falcon-specific WSGI application. See :meth:`~restart.serving.Service.wsgi_app` for the meanings of the parameters. """ return self.falcon_api(environ, start_response) def get_embedded_rules(self): """Get the Falcon-specific rules used to be embedded into an existing or legacy application. Example: # The existing Falcon API import falcon app = falcon.API() ... # The RESTArt API from restart.api import RESTArt api = RESTArt() ... # Embed RESTArt into Falcon from restart.serving import Service from restart.ext.falcon.adapter import FalconAdapter service = Service(api, FalconAdapter) for uri, resource in service.embedded_rules: app.add_route(uri, resource) """ rules = [] for endpoint, rule in iteritems(self.adapted_rules): resource = FalconResource() for method in rule.methods: on_method = types.MethodType(rule.handler, resource) setattr(resource, 'on_' + method.lower(), on_method) # Convert URI style from RESTArt's `<arg>` to Falcon's `{arg}` uri = rule.uri.replace('<', '{').replace('>', '}') assert ':' not in uri, ( 'Werkzeug-style converters (with the separator ":") is ' 'not supported in `RESTArt-Falcon`. Captured parameters ' 'in URI can only be specified in the form of "<arg>"' ) rules.append((uri, resource)) return tuple(rules)
import six from falcon import API from falcon.cmd import print_routes from falcon.testing import redirected class DummyResource(object): def on_get(self, req, resp): resp.body = 'Test\n' resp.status = '200 OK' _api = API() _api.add_route('/test', DummyResource()) def test_traverse_with_verbose(): """Ensure traverse() finds the proper routes and outputs verbose info.""" output = six.moves.StringIO() with redirected(stdout=output): print_routes.traverse(_api._router._roots, verbose=True) route, get_info, options_info = output.getvalue().strip().split('\n') assert '-> /test' == route # NOTE(kgriffs) We might receive these in either order, since the # method map is not ordered, so check and swap if necessary. if options_info.startswith('-->GET'):
from falcon import API import endpoints import pymongo class CorsMiddleware(object): def process_request(self, request, response): response.set_header('Access-Control-Allow-Origin', '*') db = pymongo.MongoClient('mongodb.docker') app = API(middleware=[CorsMiddleware()]) app.add_route('/', endpoints.BaseResponse(db)) app.add_route('/status', endpoints.ProjectStatus(db)) app.add_route('/status/{status_id}', endpoints.ProjectDetail(db))