]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/server/sanic/server.py
reorganize api submodule
[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
11 from pathlib import Path
12
13 import sanic
14
15 from nominatim.api import NominatimAPIAsync, StatusResult
16 import nominatim.result_formatter.v1 as formatting
17
18 api = sanic.Blueprint('NominatimAPI')
19
20 CONTENT_TYPE = {
21   'text': 'text/plain; charset=utf-8',
22   'xml': 'text/xml; charset=utf-8'
23 }
24
25 def usage_error(msg: str) -> sanic.HTTPResponse:
26     """ Format the response for an error with the query parameters.
27     """
28     return sanic.response.text(msg, status=400)
29
30
31 def api_response(request: sanic.Request, result: Any) -> sanic.HTTPResponse:
32     """ Render a response from the query results using the configured
33         formatter.
34     """
35     body = request.ctx.formatter.format(result, request.ctx.format)
36     return sanic.response.text(body,
37                                content_type=CONTENT_TYPE.get(request.ctx.format,
38                                                              'application/json'))
39
40
41 @api.on_request # type: ignore[misc]
42 async def extract_format(request: sanic.Request) -> Optional[sanic.HTTPResponse]:
43     """ Get and check the 'format' parameter and prepare the formatter.
44         `ctx.result_type` describes the expected return type and
45         `ctx.default_format` the format value to assume when no parameter
46         is present.
47     """
48     assert request.route is not None
49     request.ctx.formatter = request.app.ctx.formatters[request.route.ctx.result_type]
50
51     request.ctx.format = request.args.get('format', request.route.ctx.default_format)
52     if not request.ctx.formatter.supports_format(request.ctx.format):
53         return usage_error("Parameter 'format' must be one of: " +
54                            ', '.join(request.ctx.formatter.list_formats()))
55
56     return None
57
58
59 @api.get('/status', ctx_result_type=StatusResult, ctx_default_format='text')
60 async def status(request: sanic.Request) -> sanic.HTTPResponse:
61     """ Implementation of status endpoint.
62     """
63     result = await request.app.ctx.api.status()
64     response = api_response(request, result)
65
66     if request.ctx.format == 'text' and result.status:
67         response.status = 500
68
69     return response
70
71
72 def get_application(project_dir: Path,
73                     environ: Optional[Mapping[str, str]] = None) -> sanic.Sanic:
74     """ Create a Nominatim sanic ASGI application.
75     """
76     app = sanic.Sanic("NominatimInstance")
77
78     app.ctx.api = NominatimAPIAsync(project_dir, environ)
79     app.ctx.formatters = {}
80     for rtype in (StatusResult, ):
81         app.ctx.formatters[rtype] = formatting.create(rtype)
82
83     app.blueprint(api)
84
85     return app