1 # SPDX-License-Identifier: GPL-2.0-only
 
   3 # This file is part of Nominatim. (https://nominatim.org)
 
   5 # Copyright (C) 2022 by the Nominatim developer community.
 
   6 # For a full list of authors see the git log.
 
   8 Server implementation using the sanic webserver framework.
 
  10 from typing import Any, Optional, Mapping
 
  11 from pathlib import Path
 
  15 from nominatim.api import NominatimAPIAsync
 
  16 from nominatim.apicmd.status import StatusResult
 
  17 import nominatim.result_formatter.v1 as formatting
 
  19 api = sanic.Blueprint('NominatimAPI')
 
  22   'text': 'text/plain; charset=utf-8',
 
  23   'xml': 'text/xml; charset=utf-8'
 
  26 def usage_error(msg: str) -> sanic.HTTPResponse:
 
  27     """ Format the response for an error with the query parameters.
 
  29     return sanic.response.text(msg, status=400)
 
  32 def api_response(request: sanic.Request, result: Any) -> sanic.HTTPResponse:
 
  33     """ Render a response from the query results using the configured
 
  36     body = request.ctx.formatter.format(result, request.ctx.format)
 
  37     return sanic.response.text(body,
 
  38                                content_type=CONTENT_TYPE.get(request.ctx.format,
 
  42 @api.on_request # type: ignore[misc]
 
  43 async def extract_format(request: sanic.Request) -> Optional[sanic.HTTPResponse]:
 
  44     """ Get and check the 'format' parameter and prepare the formatter.
 
  45         `ctx.result_type` describes the expected return type and
 
  46         `ctx.default_format` the format value to assume when no parameter
 
  49     assert request.route is not None
 
  50     request.ctx.formatter = request.app.ctx.formatters[request.route.ctx.result_type]
 
  52     request.ctx.format = request.args.get('format', request.route.ctx.default_format)
 
  53     if not request.ctx.formatter.supports_format(request.ctx.format):
 
  54         return usage_error("Parameter 'format' must be one of: " +
 
  55                            ', '.join(request.ctx.formatter.list_formats()))
 
  60 @api.get('/status', ctx_result_type=StatusResult, ctx_default_format='text')
 
  61 async def status(request: sanic.Request) -> sanic.HTTPResponse:
 
  62     """ Implementation of status endpoint.
 
  64     result = await request.app.ctx.api.status()
 
  65     response = api_response(request, result)
 
  67     if request.ctx.format == 'text' and result.status:
 
  73 def get_application(project_dir: Path,
 
  74                     environ: Optional[Mapping[str, str]] = None) -> sanic.Sanic:
 
  75     """ Create a Nominatim sanic ASGI application.
 
  77     app = sanic.Sanic("NominatimInstance")
 
  79     app.ctx.api = NominatimAPIAsync(project_dir, environ)
 
  80     app.ctx.formatters = {}
 
  81     for rtype in (StatusResult, ):
 
  82         app.ctx.formatters[rtype] = formatting.create(rtype)