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
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 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_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_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_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 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 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 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_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 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_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 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 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 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 api(title, version): log = Log.get('api') middlewares = [Negotiation_Middleware()] if Arg_Reader.db.auth: log.notice('JWT authentication enabled') middlewares.append( Falcon_Auth_Middleware( JWT_Auth_Backend( user_loader=lambda token: {'user': token}, secret_key=Arg_Reader.db.auth_secret_key, auth_header_prefix=Arg_Reader.db.auth_header_prefix), exempt_routes=['/api/doc', '/api/doc/swagger.json'])) else: log.notice('JWT authentication disabled') if Arg_Reader.db.https: log.notice('Force to use HTTPS instead of HTTP') middlewares.append(RequireHTTPS()) else: log.notice('HTTPS not set') if Arg_Reader.db.apm_enabled: log.notice('Elastic APM enabled') middlewares.append( Elastic_Apm_Middleware(service_name='lcp-apm', server_url=Arg_Reader.db.apm_server)) else: log.notice('Elastic APM disabled') instance = API(middleware=middlewares) media_handlers = { falcon.MEDIA_JSON: JSON_Handler(loads=loads, dumps=partial(dumps, ensure_ascii=False, sort_keys=True)), falcon.MEDIA_MSGPACK: Message_Pack_Handler(), falcon.MEDIA_XML: XML_Handler(), falcon.MEDIA_YAML: YAML_Handler() } instance.req_options.media_handlers.update(media_handlers) instance.resp_options.media_handlers.update(media_handlers) instance.add_error_handler(*Bad_Request_Handler.get()) instance.add_error_handler(*Internal_Server_Error_Handler.get()) instance.add_error_handler(*Unsupported_Media_Type_Handler.get()) api_spec = Spec(api=instance, title=title, version=version) routes(api=instance, spec=api_spec.get()) falcon_api_doc(instance, config_path='./swagger/schema.json', url_prefix='/api/doc', title='API doc') api_spec.write() return instance
def register_routes(falcon_api: API, get_current_model: GetModel): falcon_api.add_route('/', SwaggerController()) falcon_api.add_route('/api/hc', MonitorController()) falcon_api.add_route( '/api/mlmodel', DummyModelController(get_current_model=get_current_model)) falcon_api.add_static_route('/static', STATIC_APPLICATION_PATH)
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 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 test_empty_requests_dropped(self, empty_body, endpoint, endpoint_exists): # type: (bool, str, bool) -> None """Test that empty requests are dropped before routing Note that content_length is None if not explicitly set, so HEAD, GET, etc. requests are fine. """ client = testing.TestClient(API(middleware=[m.EmptyRequestDropper()])) # We have to forge the content-length header, because if it is # made automatically for our empty body, we won't get to the check # of the body contents. heads = {"Content-Length": str("20")} if empty_body else {} body = "" if empty_body else None res = client.simulate_get(endpoint, body=body, headers=heads) # type: testing.Result if empty_body: assert res.status == status_codes.HTTP_BAD_REQUEST else: if endpoint_exists: assert bool( 200 <= res.status_code < 300 or res.status_code == status_codes.HTTP_METHOD_NOT_ALLOWED) else: assert res.status == status_codes.HTTP_NOT_FOUND
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 test_simulate_request_http_version(version, valid): app = API() if valid: testing.simulate_request(app, http_version=version) else: with pytest.raises(ValueError): testing.simulate_request(app, http_version=version)
def test_get_remote_addr(limiter): """ Test the utils.get_remote_addr() function which returns the requestor's ip Create an app with the default limiter and a method which is calling the get_remote_addr() """ @limiter.limit() class ThingsResource: def on_get(self, req, resp): resp.body = 'Hello world!' assert '127.0.0.1' == get_remote_addr(req, resp, None, None) app = API(middleware=limiter.middleware) things = ThingsResource() app.add_route('/things', things) client = testing.TestClient(app) client.simulate_get('/things')
def add_routes(api: falcon.API): storage = DataStorage() job_queue = deque() start_worker(storage, job_queue) api.add_route("/", SegmentationBase()) api.add_route("/docs", SegmentationDocs()) # Note: falcon internally strips trailing slashes from requests api.add_route("/jobs", SegmentationJobs(storage, job_queue)) api.add_route("/jobs/{job_id}", SegmentationJob(storage))
def serve(port: int = 3000, root_dir: Opt[str] = None) -> None: ''' Start a server providing access to the records in a directory. ''' root_dir = Path(root_dir or get_conf().root_dir) def write_response(req: Request, res: Response) -> None: res.content_type = 'application/cbor' res.set_header('Access-Control-Allow-Origin', '*') if req.path.endswith('/_entry-names'): path = root_dir / req.path[1:-len('/_entry-names')] if path.is_file(): raise HTTPStatus(HTTP_404) res.data = cbor2.dumps(dict( type='plain-object', content=sorted([ re.sub(r'\.h5$', '', p.name) + ('/' if p.is_dir() else '') for p in path.glob('[!_]*') ]) )) elif req.path.endswith('/_meta'): key = req.path[1:-len('/_meta')] res.data = cbor2.dumps(_read_meta(root_dir, key)) else: t_last = float(req.get_param('t_last') or 0) / 1000 entry = _read(root_dir, req.path[1:], t_last) if entry['type'] == 'file': res.data = (root_dir / entry['content']).read_bytes() else: res.data = cbor2.dumps(entry) res.status = HTTP_200 app = API(middleware=[_HandleCORS()]) app.add_sink(write_response) class Server(GunicornApp): # type: ignore def load(self) -> API: return app def load_config(self) -> None: self.cfg.set('bind', f'localhost:{port}') self.cfg.set('workers', 2 * cpu_count()) Server().run()
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 test_default_dynamic_limits(): """ Test using the default_dynamic_limits option to change the limit per user """ limiter = Limiter(key_func=get_remote_addr, default_dynamic_limits=lambda req, resp, resource, req_succeeded: '5/second' if req.get_header('APIUSER') == 'admin' else '2/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) #### # '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
def test_content_type_json_required(self, endpoint, method, endpoint_exists, ct_is_json, required): # type: (str, str, bool, bool, bool) -> None """Test HTTP methods that should reuquire content-type json Note ``required`` is always true at the moment, because the middleware applies to all requests with no exceptions. If that changes, add parameters above. The endpoint here does not really matter, because ``process_request`` middleware, which handles enforcement of the accept header, is processed before routing. However, the ``endpoint_exists`` option is provided in case it is necessary in the future to ensure this is called against a real endpoint. """ client = testing.TestClient(API(middleware=[m.JSONEnforcer()])) if ct_is_json: heads = {'Content-Type': str('application/json')} else: heads = {} res = client.simulate_request( method=str(method), path=endpoint, headers=heads ) # type: testing.Result meth_not_allowed = status_codes.HTTP_METHOD_NOT_ALLOWED if required: if method in m.JSON_CONTENT_REQUIRED_METHODS: # Ensure that non-json content type is denied if ct_is_json: if endpoint_exists: assert bool( 200 <= res.status_code < 300 or res.status_code == meth_not_allowed ) else: assert res.status == status_codes.HTTP_NOT_FOUND else: exp_code = status_codes.HTTP_UNSUPPORTED_MEDIA_TYPE assert res.status == exp_code else: # We shouldn't deny for non-json content type if endpoint_exists: assert bool( 200 <= res.status_code < 300 or res.status_code == meth_not_allowed ) else: assert res.status == status_codes.HTTP_NOT_FOUND else: # No endpoints for which this is applicable exist at the moment pass
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)
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)
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))
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()
def client(): api = API(middleware=ValidatorComponent()) api.add_route(SONGFINISH_ROUTE, SongFinish(token=TOKEN)) return testing.TestClient(api)
def __init__(*args, **kwargs): if 'logger' in kwargs: #FIXME: add check to see if subclass() of logger.*some_logger* args[0]._logger = kwargs['logger'] del kwargs['logger'] Falcon_API.__init__(*args, **kwargs)
def __init__(self, options=None): self.falcon = API() self.options = options or {} self.register_resources() super(Server, self).__init__()