graphinate.renderers.graphql._starlette_app()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 31
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 31
rs 9.328
c 0
b 0
f 0
cc 3
nop 3
1
import contextlib
2
import webbrowser
3
4
import strawberry
5
from starlette.applications import Starlette
6
from starlette.requests import Request
7
from starlette.responses import RedirectResponse
8
from starlette.schemas import SchemaGenerator
9
from starlette.types import ASGIApp
10
from strawberry.asgi import GraphQL
11
12
from graphinate.server.starlette import routes
13
14
DEFAULT_PORT: int = 8072
15
16
GRAPHQL_ROUTE_PATH = "/graphql"
17
18
19
def _openapi_schema(request: Request) -> ASGIApp:
20
    """
21
    Generates an OpenAPI schema for the GraphQL API and other routes.
22
23
    Args:
24
        request (Request): The HTTP request object.
25
26
    Returns:
27
        ASGIApp: An OpenAPI response containing the schema for the specified routes.
28
    """
29
    schema_data = {
30
        'openapi': '3.0.0',
31
        'info': {'title': 'Graphinate API', 'version': '0.9.0'},
32
        'paths': {
33
            '/graphql': {'get': {'responses': {200: {'description': 'GraphQL'}}}},
34
            '/graphiql': {'get': {'responses': {200: {'description': 'GraphiQL UI.'}}}},
35
            '/metrics': {'get': {'responses': {200: {'description': 'Prometheus metrics.'}}}},
36
            '/viewer': {'get': {'responses': {200: {'description': '3D Force-Directed Graph Viewer'}}}},
37
            '/voyager': {'get': {'responses': {200: {'description': 'Voyager GraphQL Schema Viewer'}}}}
38
        }
39
    }
40
41
    schema = SchemaGenerator(schema_data)
42
    return schema.OpenAPIResponse(request=request)
43
44
45
def _graphql_app(graphql_schema: strawberry.Schema) -> strawberry.asgi.GraphQL:
46
    """
47
    Creates a Strawberry GraphQL app with the provided schema.
48
    Args:
49
        graphql_schema:
50
51
    Returns:
52
        strawberry.asgi.GraphQL: The GraphQL app configured with the provided schema.
53
    """
54
    graphql_app = GraphQL(graphql_schema, graphiql=True)
55
    return graphql_app
56
57
58
def _starlette_app(graphql_app: strawberry.asgi.GraphQL | None = None, port: int = DEFAULT_PORT, **kwargs) -> Starlette:
59
    def open_url(endpoint):
60
        webbrowser.open(f'http://localhost:{port}/{endpoint}')
61
62
    @contextlib.asynccontextmanager
63
    async def lifespan(app: Starlette):  # pragma: no cover
64
        if kwargs.get('browse'):
65
            open_url('viewer')
66
        yield
67
68
    app = Starlette(
69
        lifespan=lifespan,
70
        routes=routes()
71
    )
72
73
    from starlette_prometheus import PrometheusMiddleware, metrics
74
    app.add_middleware(PrometheusMiddleware)
75
    app.add_route("/metrics", metrics)
76
77
    if graphql_app:
78
        app.add_route(GRAPHQL_ROUTE_PATH, graphql_app)
79
        app.add_websocket_route(GRAPHQL_ROUTE_PATH, graphql_app)
80
        app.add_route("/schema", route=_openapi_schema, include_in_schema=False)
81
        app.add_route("/openapi.json", route=_openapi_schema, include_in_schema=False)
82
83
    async def redirect_to_viewer(request):
84
        return RedirectResponse(url='/viewer')
85
86
    app.add_route('/', redirect_to_viewer)
87
88
    return app
89
90
91
def server(graphql_schema: strawberry.Schema, port: int = DEFAULT_PORT, **kwargs):
92
    """
93
    Args:
94
        graphql_schema: The Strawberry GraphQL schema.
95
        port: The port number to run the server on. Defaults to 8072.
96
97
    Returns:
98
    """
99
100
    graphql_app = _graphql_app(graphql_schema)
101
102
    app = _starlette_app(graphql_app, port=port, **kwargs)
103
104
    import uvicorn
105
    uvicorn.run(app, host='0.0.0.0', port=port)
106
107
108
__all__ = ['server']
109