def setup_app(): setup_logging() cookie_store = CookieStore(**settings.strict_get("sessions")) get_schema = OpenAPIHandler( Metadata( title="Chat", description="A simple chat app.", version="0.0.0", ), ) get_docs = OpenAPIUIHandler() app = App( components=[ AccountManagerComponent(), ChatHandlerFactoryComponent(), ChatroomListenerComponent(), ChatroomRegistryComponent(), CurrentAccountComponent(), PasswordHasherComponent(), RedisComponent(), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), SessionComponent(cookie_store), SettingsComponent(settings), TemplatesComponent(path_to("templates")), ], middleware=[ RequestIdMiddleware(), SessionMiddleware(cookie_store), ResponseRendererMiddleware(), WebsocketsMiddleware(), SQLAlchemyMiddleware(), ], routes=[ Route("/_schema", get_schema), Route("/_docs", get_docs), Route("/", index), Route("/login", login), Route("/register", register), Include("/v1", [ Include("/accounts", accounts.routes, namespace="accounts"), Include("/chat", chat.routes, namespace="chat"), Include("/sessions", sessions.routes, namespace="sessions"), ], namespace="v1"), ], ) decorated_app = WhiteNoise(app, **settings.strict_get("whitenoise")) return decorated_app, app
def test_apps_session_cookies_expire(): # Given that I have an app with a cookie store that immediately expires session cookies cookie_store = CookieStore(b"secret", cookie_ttl=0) def set_username(username: str, session: Session) -> None: session["username"] = username def get_username(session: Session) -> Optional[str]: return session.get("username") app = App( components=[ SessionComponent(cookie_store), ], middleware=[ SessionMiddleware(cookie_store), ResponseRendererMiddleware(), ], routes=[ Route("/set-username/{username}", set_username), Route("/get-username", get_username), ], ) # And a client for that app client = testing.TestClient(app) # When I make a request to a handler that stores session data response = client.get(app.reverse_uri("set_username", username="******")) # Then I should get back a successful response assert response.status_code == 200 # And the response should contain my session cookie cookie = cookies.SimpleCookie() for data in response.headers.get_all("set-cookie"): cookie.load(data.replace("SameSite=Strict", "")) assert "__sess__" in cookie # When I make another request with that same cookie session_cookie = cookie.output(attrs=[], header="") response = client.get(app.reverse_uri("get_username"), headers={ "cookie": session_cookie, }) # Then I should get back nothing assert response.json() is None
def test_middleware_token_validation_raises_error_on_token_exp(app_settings): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id jwt = JWT(key="keepthissafe", alg="HS256") iat = dt.datetime.now() + dt.timedelta(seconds=-10) exp = iat + dt.timedelta(seconds=5) payload = {"sub": "1234567890", "name": "John Doe", "iat": iat, "exp": exp} token = jwt.encode(payload) routes = [Route("/auth-required", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-required", headers={"Authorization": f"Bearer {token}"}) assert 401 == response.status_code content = response.json() assert "error_message" in content assert content.get("status") == 401
def test_openapi_can_render_fields_with_different_request_and_response_names(): # Given that I have a schema that has different names based on whether it's in the request or response @schema class A: x: int = field(request_name="X", response_name="Y") def index(a: A) -> A: pass # And an app app = App(routes=[Route("/", index)]) # When I generate a document document = generate_openapi_document( app, Metadata("example", "an example", "0.0.0"), []) # Then the schema should mark that field as writeOnly response_schema = document["components"]["schemas"][ "tests.openapi.test_openapi.A"] assert response_schema["properties"] == { "X": { "type": "integer", "format": "int64", "writeOnly": True, }, "Y": { "type": "integer", "format": "int64", "readOnly": True, }, }
def test_openapi_can_render_request_only_fields(): # Given that I have a schema that has request-only fields @schema class A: x: int = field(request_only=True) def index(a: A) -> A: pass # And an app app = App(routes=[Route("/", index)]) # When I generate a document document = generate_openapi_document( app, Metadata("example", "an example", "0.0.0"), []) # Then the schema should mark that field as writeOnly response_schema = document["components"]["schemas"][ "tests.openapi.test_openapi.A"] assert response_schema["properties"] == { "x": { "type": "integer", "format": "int64", "writeOnly": True, }, }
def test_middleware_white_listing(app_settings, testing_token): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id routes = [Route("/whitelisted", method="GET", handler=test_handler)] components = [ SettingsComponent({ **app_settings, "JWT_AUTH_WHITELIST": ["test_handler"] }), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/whitelisted", headers={"Authorization": f"Bearer {testing_token}"}) unauthenticated = client.get("/whitelisted") assert 200 == response.status_code assert "1234567890" in response.data assert 200 == unauthenticated.status_code assert "No user token present" in unauthenticated.data
def _create_molten_routes() -> Sequence[Route]: routes = [] for endpoint in _ENDPOINTS: url = endpoint.method.__url__ # type: ignore # noqa: WPS609 method = endpoint.method.__method__ # type: ignore # noqa: WPS609 routes.append(Route(url, endpoint, method=method)) return routes
def test_router_can_match_nested_routes(): # Given that I have a router with some nested routes router = Router([ Include("/v1", [ Include("/accounts", [ Route("/", handler, name="get_accounts"), Route("/{account_id}", handler, name="get_account"), ]), ]), ]) # When I match either of the routes # Then I should get back their Route objects and path params assert router.match("GET", "/v1/accounts/") assert router.match("GET", "/v1/accounts/1") # When I match a route that doesn't exist # Then I should get back None assert router.match("GET", "/v1") is None
def test_empty_app_can_return_openapi_document(): # Given that I have an empty app app = App(routes=[ Route("/schema.json", OpenAPIHandler( Metadata( title="empty application", description="an application that doesn't do anything", version="0.1.0", contact=Contact(name="Jim Gordon", ), )), name="schema"), ]) # When I visit its schema uri response = testing.TestClient(app).get("/schema.json") # Then I should get back a successful response assert response.status_code == 200 assert response.json() == { "openapi": "3.0.1", "info": { "title": "empty application", "description": "an application that doesn't do anything", "version": "0.1.0", "contact": { "name": "Jim Gordon", }, }, "paths": { "/schema.json": { "get": { "tags": [], "operationId": "schema", "summary": "Generates an OpenAPI v3 document.", "description": "", "deprecated": False, "parameters": [], "responses": { "200": { "description": "A successful response.", "content": {}, }, }, } } }, "components": { "schemas": {}, "securitySchemes": {}, }, }
def test_identity_extract_jwt_from_cookie(app_settings): def test_auth(jwt: JWT): cookie_name = "molten_auth_cookie" cookie_value = jwt.encode({"sub": 123456, "name": "spiderman"}) auth_response = Response(HTTP_200) auth_response.set_cookie(Cookie(cookie_name, cookie_value)) return auth_response def test_cookie(jwt_identity: JWTIdentity): if jwt_identity is None: return "Didn't work" return f"Hello {jwt_identity.name} your sub id is {jwt_identity.sub}" routes = [ Route("/auth", method="POST", handler=test_auth), Route("/cookie", method="GET", handler=test_cookie), ] components = [ SettingsComponent({ **app_settings, **{ "JWT_AUTH_COOKIE": "molten_auth_cookie" } }), JWTComponent(), JWTIdentityComponent(), ] app = App(routes=routes, components=components) client = testing.TestClient(app) auth_response = client.post("/auth") cookie_value = auth_response.headers.get_all("set-cookie")[0] assert "molten_auth_cookie" in cookie_value cookie_response = client.get("/cookie", headers={"cookie": cookie_value}) assert "123456" in cookie_response.data assert "spiderman" in cookie_response.data
def setup_app(): setup_logging() get_docs = OpenAPIUIHandler() get_schema = OpenAPIHandler( metadata=Metadata( title="Pets", description="", version="0.0.0", ), ) app = App( components=[ ManagerComponent(PetManager), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), SettingsComponent(settings), TemplatesComponent(path_to("templates")), ], middleware=[ RequestIdMiddleware(), ResponseRendererMiddleware(), SQLAlchemyMiddleware(), ], routes=[ Route("/_docs", get_docs), Route("/_schema", get_schema), Route("/", index), Include("/v1/pets", pets.routes, namespace="pets"), ], ) decorated_app = WhiteNoise(app, **settings.strict_get("whitenoise")) return decorated_app, app
def test_claims_required_attaches_values(app_settings): @claims_required({"admin": True}) def test_handler(): return "Handler called" routes = [Route("/claims", method="GET", handler=test_handler)] components = [SettingsComponent(app_settings)] middleware = [ResponseRendererMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/claims") assert 200 == response.status_code assert "admin" in test_handler.claims assert test_handler.claims.get("admin") is True assert "Handler called" in response.data
def test_middleware_raises_401_error(app_settings): def test_handler(): return "Handler called" routes = [Route("/auth-required", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-required") assert 401 == response.status_code
def test_openapi_can_render_documents_with_method_handlers(): # Given that I have a resource class @schema class User: username: str class Users: def get_users(self) -> User: pass # And an app that uses that an instance of that resource users = Users() app = App(routes=[Route("/users", users.get_users)]) # When I generate a document document = generate_openapi_document( app, Metadata("example", "an example", "0.0.0"), []) # Then I should get back a valid document assert document
def test_openapi_can_render_lists_of_x(fields, expected): # Given that I have a schema that has a list of something in it A = type("A", (object, ), fields) A.__annotations__ = fields A = schema(A) def index() -> A: pass # And an app app = App(routes=[Route("/", index)]) # When I generate a document document = generate_openapi_document( app, Metadata("example", "an example", "0.0.0"), []) # Then the return schema should have an array of that thing response_schema = document["components"]["schemas"][ "tests.openapi.test_openapi.A"] assert response_schema["properties"] == expected
def test_JWT_claims_options_raises_error(app_settings, testing_token): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id jwt = JWT(key=app_settings.get("JWT_SECRET_KEY"), alg="HS256") mod_token = jwt.encode({ **jwt.decode(testing_token), **{ "iss": "https://molten.com" } }) routes = [Route("/claim_options", method="GET", handler=test_handler)] components = [ SettingsComponent({ **app_settings, "JWT_CLAIMS_OPTIONS": { "iss": { "essential": True, "values": ["https://example.com", "https://example.org"], } }, }), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) missing_claim_response = client.get( "/claim_options", headers={"Authorization": f"Bearer {testing_token}"}) wrong_claim_value_response = client.get( "/claim_options", headers={"Authorization": f"Bearer {mod_token}"}) assert 401 == missing_claim_response.status_code assert 401 == wrong_claim_value_response.status_code
def test_middleware_anonymous_user_support(app_settings): @allow_anonymous def test_handler(): return "Handler called" routes = [Route("/auth-maybe", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-maybe") assert 200 == response.status_code assert "Handler called" in response.data
def test_claims_required_raises_error(app_settings, testing_token): @claims_required({"admin": True}) def test_handler(): return "Handler called" routes = [Route("/claims", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/claims", headers={"Authorization": f"Bearer {testing_token}"}) assert 403 == response.status_code
def create_app(middleware=None, components=None, settings=None): if settings is None: settings = Settings({ 'database_engine_dsn': os.environ['SQLALCHEMY_URI'], 'identity_server': os.environ['IDENTITY_SERVER'], 'secret_key': os.environ['SECRET_KEY'], }) if middleware is None: middleware = [ ResponseRendererMiddleware(), SQLAlchemyMiddleware(), auth_middleware, ] if components is None: components = [ SettingsComponent(settings), SQLAlchemyEngineComponent(), SQLAlchemySessionComponent(), RequestSessionComponent(), AuthProviderComponent(), ManagerComponent(ChoreInstanceManager), ManagerComponent(ChoreDefinitionManager), UserProviderComponent(), ] app = App( routes=[ Route('/login', login, method='POST', name='login'), Include( '/api', routes, namespace='api', ) ], middleware=middleware, components=components, ) return app
def test_ws_middleware_validates_origin(): # Given that I have an app instance whose ws middleware validates the incoming origin app = App( middleware=[ ResponseRendererMiddleware(), WebsocketsMiddleware(re.compile("example.com")), ], routes=[Route("/echo", echo)], ) # And a ws client for that app client = WebsocketsTestClient(app) # When I try to connect to its echo endopoint with an invalid origin # Then an error should occur with pytest.raises(ValueError): client.connect("/echo") # When I try to connect to its echo endopoint with a valid origin with client.connect("/echo", headers={"origin": "example.com"}) as sock: # Then my connection should succeed assert sock
def test_middleware_token_validation_passes(app_settings, testing_token): def test_handler(jwt_identity: JWTIdentity): if jwt_identity is None: return "No user token present" return jwt_identity.id routes = [Route("/auth-required", method="GET", handler=test_handler)] components = [ SettingsComponent(app_settings), JWTComponent(), JWTIdentityComponent(), ] middleware = [ResponseRendererMiddleware(), JWTAuthMiddleware()] app = App(routes=routes, components=components, middleware=middleware) client = testing.TestClient(app) response = client.get("/auth-required", headers={"Authorization": f"Bearer {testing_token}"}) assert 200 == response.status_code assert "1234567890" in response.data
def test_app_can_render_mail_templates(): mail = Mail( user="******", password="******", port=587, use_tls=True, suppress_send=True, ) mail.send = MagicMock(return_value=None) def template_handler(mail_templates: MailTemplates) -> Response: mail.send_message( subject="Test email", html=mail_templates.render("test_template.html", name="Molten"), recipients=["*****@*****.**"], ) return Response(HTTP_204, content="") app = App( components=[MailTemplatesComponent("./tests/mail_templates")], routes=[Route("/", template_handler, name="index")], ) client = testing.TestClient(app) # Given that a handler will use templating # When constructing and email response = client.get(app.reverse_uri("index")) # Then I should get back a successfull response assert response.status_code == 204 # and the handler should have called the Mail object # with the string value of the rendered template mail_msg = mail.send.call_args[0][0] assert "<th>Hey there Molten</th>" in mail_msg.html
def test_missing_auth_cookie(app_settings): def test_cookie(jwt_identity: JWTIdentity): if jwt_identity is None: return "Didn't work" return f"Hello {jwt_identity.name} your sub id is {jwt_identity.sub}" routes = [Route("/cookie", method="GET", handler=test_cookie)] components = [ SettingsComponent({ **app_settings, **{ "JWT_AUTH_COOKIE": "molten_auth_cookie" } }), JWTComponent(), JWTIdentityComponent(), ] app = App(routes=routes, components=components) client = testing.TestClient(app) cookie_response = client.get("/cookie") assert "Didn't work" in cookie_response.data
while not ws.closed: message = ws.receive() if isinstance(message, CloseMessage): return ws.send(message) app = App( middleware=[ ResponseRendererMiddleware(), WebsocketsMiddleware(), ], routes=[ Route("/", index), Route("/echo", echo), ] ) client = WebsocketsTestClient(app) def test_ws_routes_return_bad_request_if_upgrade_is_not_requested(): # Given that I have a ws endpoint # When I make a standard HTTP request to that endpoint response = client.get("/echo") # Then I should get back a Bad Request response assert response.status_code == 400
def get_chores(chore_manager: ChoreDefinitionManager) -> Chore: chores = chore_manager.get_chores() return [Chore.from_chore_model(chore_model) for chore_model in chores] def create_chore(chore: Chore, chore_manager: ChoreDefinitionManager) -> Chore: chore_model = chore_manager.persist_chore(chore) return Chore.from_chore_model(chore_model) def delete_chore(chore_id: str, chore_manager: ChoreDefinitionManager) -> str: chore_manager.delete_chore(chore_id) return '' routes = [ Route('/upcoming', get_upcoming, method='GET', name='get-upcoming'), Route('/upcoming/{chore_id}', complete_upcoming, method='POST', name='complete-upcoming'), Route('/chores', get_chores, method='GET', name='get-chores'), Route('/chores', create_chore, method='POST', name='create-chore'), Route('/chores/{chore_id}', get_chore, method='GET', name='get-chore'), Route('/chores/{chore_id}', delete_chore, method='DELETE', name='delete-chore'), ]
from molten import App, Route def hello(name: str, age: int) -> str: return f"Hello {age} year old named {name}!" app = App(routes=[Route("/hello/{name}/{age}", hello)])
from molten import App, JSONRenderer, QueryParam, ResponseRendererMiddleware, Route from molten.contrib.sessions import CookieStore, Session, SessionComponent, SessionMiddleware cookie_store = CookieStore(b"ubersecret") def set_username(username: QueryParam, session: Session) -> str: session["username"] = username return username def get_username(session: Session) -> Optional[str]: return session.get("username") app = App( components=[ SessionComponent(cookie_store), ], middleware=[ SessionMiddleware(cookie_store), ResponseRendererMiddleware([ JSONRenderer(), ]), ], routes=[ Route("/get-username", get_username), Route("/set-username", set_username), ], )
"MAIL_PASSWORD": "******", "MAIL_PORT": 587, "MAIL_USE_TLS": True, "MAIL_DEFAULT_SENDER": "*****@*****.**", } ) def send_message(params: QueryParams, mail: Mail): """Emails an email address provided in the query string""" addresses = params.get_all("email") if not addresses: return Response( HTTP_400, content="Provide emails in the query params to send a welcome message", ) msg = Message( subject="Welcome to Molten!", body="Welcome to Molten! Glad to have you here.", recipients=addresses, ) mail.send(msg) return Response(HTTP_204, content="") routes = [Route("/", send_message, "POST")] components = [SettingsComponent(settings), MailComponent()] app = App(routes=routes, components=components)
title="Emeeting API", description="An API for managing your room meetings.", version="0.0.1", )) get_docs = OpenAPIUIHandler() """ Add middlewares """ middlewares = [prometheus_middleware, ResponseRendererMiddleware()] """ Include or add routes """ routes = [ Route("/", get_docs), Route("/schema", get_schema), Route("/metrics", expose_metrics), Include("/v1/rooms", routes=room.routes), Include("/v1/meetings", routes=meeting.routes), ] """ Start application """ app = App(routes=routes, middleware=middlewares) app = CORS(app, headers="*", methods="*", origin="*", maxage="86400") log.info("Start application successfully.")
def delete_todo(todo_id: int, todo_manager: TodoManager): todo_manager.delete_todo(todo_id) return ( HTTP_202, APIResponse(status=202, message=f"Delete request for todo: {todo_id} accepted"), ) def get_todo_by_id(todo_id: int, todo_manager: TodoManager) -> Todo: try: _todo = todo_manager.get_todo_by_id(todo_id) except EntityNotFound as err: raise HTTPError(HTTP_404, APIResponse(status=404, message=err.message) ) return _todo def update_todo(todo_id: int, todo: Todo, todo_manager: TodoManager) -> Todo: return todo_manager.update_todo(todo_id, todo) todo_routes = Include("/todos", [ Route("", list_todos, method="GET"), Route("", create_todo, method="POST"), Route("/{todo_id}", delete_todo, method="DELETE"), Route("/{todo_id}", get_todo_by_id, method="GET"), Route("/{todo_id}", update_todo, method="PATCH") ])