graphinate.renderers.graphql   A
last analyzed

Complexity

Total Complexity 6

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 60
dl 0
loc 112
rs 10
c 0
b 0
f 0

4 Functions

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