]> git.openstreetmap.org Git - nominatim.git/blob - nominatim/server/falcon/server.py
reorganize code around result formatting
[nominatim.git] / nominatim / server / falcon / 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 falcon webserver framework.
9 """
10 from typing import Type, Any, Optional, Mapping
11 from pathlib import Path
12
13 import falcon
14 import falcon.asgi
15
16 from nominatim.api import NominatimAPIAsync, StatusResult
17 import nominatim.api.v1 as api_impl
18
19 CONTENT_TYPE = {
20   'text': falcon.MEDIA_TEXT,
21   'xml': falcon.MEDIA_XML
22 }
23
24 class NominatimV1:
25     """ Implementation of V1 version of the Nominatim API.
26     """
27
28     def __init__(self, project_dir: Path, environ: Optional[Mapping[str, str]]) -> None:
29         self.api = NominatimAPIAsync(project_dir, environ)
30
31
32     def parse_format(self, req: falcon.asgi.Request, rtype: Type[Any], default: str) -> None:
33         """ Get and check the 'format' parameter and prepare the formatter.
34             `rtype` describes the expected return type and `default` the
35             format value to assume when no parameter is present.
36         """
37         req.context.format = req.get_param('format', default=default)
38
39         if not api_impl.supports_format(rtype, req.context.format):
40             raise falcon.HTTPBadRequest(
41                 description="Parameter 'format' must be one of: " +
42                             ', '.join(api_impl.list_formats(rtype)))
43
44
45     def format_response(self, req: falcon.asgi.Request, resp: falcon.asgi.Response,
46                         result: Any) -> None:
47         """ Render response into a string according to the formatter
48             set in `parse_format()`.
49         """
50         resp.text = api_impl.format_result(result, req.context.format)
51         resp.content_type = CONTENT_TYPE.get(req.context.format, falcon.MEDIA_JSON)
52
53
54     async def on_get_status(self, req: falcon.asgi.Request, resp: falcon.asgi.Response) -> None:
55         """ Implementation of status endpoint.
56         """
57         self.parse_format(req, StatusResult, 'text')
58
59         result = await self.api.status()
60
61         self.format_response(req, resp, result)
62         if result.status and req.context.format == 'text':
63             resp.status = 500
64
65
66 def get_application(project_dir: Path,
67                     environ: Optional[Mapping[str, str]] = None) -> falcon.asgi.App:
68     """ Create a Nominatim falcon ASGI application.
69     """
70     app = falcon.asgi.App()
71
72     api = NominatimV1(project_dir, environ)
73
74     app.add_route('/status', api, suffix='status')
75
76     return app