]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/server/sanic/server.py
allow to add php-compatible endpoints
[nominatim.git] / nominatim / server / sanic / server.py
1 # SPDX-License-Identifier: GPL-2.0-only
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2023 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Server implementation using the sanic webserver framework.
9 """
10 from typing import Any, Optional, Mapping, Callable, cast, Coroutine
11 from pathlib import Path
12
13 from sanic import Request, HTTPResponse, Sanic
14 from sanic.exceptions import SanicException
15 from sanic.response import text as TextResponse
16
17 from nominatim.api import NominatimAPIAsync
18 import nominatim.api.v1 as api_impl
19
20 class ParamWrapper(api_impl.ASGIAdaptor):
21     """ Adaptor class for server glue to Sanic framework.
22     """
23
24     def __init__(self, request: Request) -> None:
25         self.request = request
26
27
28     def get(self, name: str, default: Optional[str] = None) -> Optional[str]:
29         return cast(Optional[str], self.request.args.get(name, default))
30
31
32     def get_header(self, name: str, default: Optional[str] = None) -> Optional[str]:
33         return cast(Optional[str], self.request.headers.get(name, default))
34
35
36     def error(self, msg: str) -> SanicException:
37         return SanicException(msg, status_code=400)
38
39
40     def create_response(self, status: int, output: str,
41                         content_type: str) -> HTTPResponse:
42         return TextResponse(output, status=status, content_type=content_type)
43
44
45 def _wrap_endpoint(func: api_impl.EndpointFunc)\
46        -> Callable[[Request], Coroutine[Any, Any, HTTPResponse]]:
47     async def _callback(request: Request) -> HTTPResponse:
48         return cast(HTTPResponse, await func(request.app.ctx.api, ParamWrapper(request)))
49
50     return _callback
51
52
53 def get_application(project_dir: Path,
54                     environ: Optional[Mapping[str, str]] = None) -> Sanic:
55     """ Create a Nominatim sanic ASGI application.
56     """
57     app = Sanic("NominatimInstance")
58
59     app.ctx.api = NominatimAPIAsync(project_dir, environ)
60
61     if app.ctx.api.config.get_bool('CORS_NOACCESSCONTROL'):
62         from sanic_cors import CORS # pylint: disable=import-outside-toplevel
63         CORS(app)
64
65     legacy_urls = app.ctx.api.config.get_bool('SERVE_LEGACY_URLS')
66     for name, func in api_impl.ROUTES:
67         endpoint = _wrap_endpoint(func)
68         app.add_route(endpoint, f"/{name}", name=f"v1_{name}_simple")
69         if legacy_urls:
70             app.add_route(endpoint, f"/{name}.php", name=f"v1_{name}_legacy")
71
72     return app