mirror of
https://github.com/osm-search/Nominatim.git
synced 2026-03-11 13:24:07 +00:00
extend BDD API tests to query via Python frameworks
A new config option ENGINE allows to choose between php and any of the supported Python engines.
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
"""
|
"""
|
||||||
Server implementation using the falcon webserver framework.
|
Server implementation using the falcon webserver framework.
|
||||||
"""
|
"""
|
||||||
from typing import Type, Any
|
from typing import Type, Any, Optional, Mapping
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import falcon
|
import falcon
|
||||||
@@ -26,8 +26,8 @@ class NominatimV1:
|
|||||||
""" Implementation of V1 version of the Nominatim API.
|
""" Implementation of V1 version of the Nominatim API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_dir: Path) -> None:
|
def __init__(self, project_dir: Path, environ: Optional[Mapping[str, str]]) -> None:
|
||||||
self.api = NominatimAPIAsync(project_dir)
|
self.api = NominatimAPIAsync(project_dir, environ)
|
||||||
self.formatters = {}
|
self.formatters = {}
|
||||||
|
|
||||||
for rtype in (StatusResult, ):
|
for rtype in (StatusResult, ):
|
||||||
@@ -67,12 +67,13 @@ class NominatimV1:
|
|||||||
self.format_response(req, resp, result)
|
self.format_response(req, resp, result)
|
||||||
|
|
||||||
|
|
||||||
def get_application(project_dir: Path) -> falcon.asgi.App:
|
def get_application(project_dir: Path,
|
||||||
|
environ: Optional[Mapping[str, str]] = None) -> falcon.asgi.App:
|
||||||
""" Create a Nominatim falcon ASGI application.
|
""" Create a Nominatim falcon ASGI application.
|
||||||
"""
|
"""
|
||||||
app = falcon.asgi.App()
|
app = falcon.asgi.App()
|
||||||
|
|
||||||
api = NominatimV1(project_dir)
|
api = NominatimV1(project_dir, environ)
|
||||||
|
|
||||||
app.add_route('/status', api, suffix='status')
|
app.add_route('/status', api, suffix='status')
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"""
|
"""
|
||||||
Server implementation using the sanic webserver framework.
|
Server implementation using the sanic webserver framework.
|
||||||
"""
|
"""
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Mapping
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import sanic
|
import sanic
|
||||||
@@ -64,12 +64,13 @@ async def status(request: sanic.Request) -> sanic.HTTPResponse:
|
|||||||
return api_response(request,await request.app.ctx.api.status())
|
return api_response(request,await request.app.ctx.api.status())
|
||||||
|
|
||||||
|
|
||||||
def get_application(project_dir: Path) -> sanic.Sanic:
|
def get_application(project_dir: Path,
|
||||||
|
environ: Optional[Mapping[str, str]] = None) -> sanic.Sanic:
|
||||||
""" Create a Nominatim sanic ASGI application.
|
""" Create a Nominatim sanic ASGI application.
|
||||||
"""
|
"""
|
||||||
app = sanic.Sanic("NominatimInstance")
|
app = sanic.Sanic("NominatimInstance")
|
||||||
|
|
||||||
app.ctx.api = NominatimAPIAsync(project_dir)
|
app.ctx.api = NominatimAPIAsync(project_dir, environ)
|
||||||
app.ctx.formatters = {}
|
app.ctx.formatters = {}
|
||||||
for rtype in (StatusResult, ):
|
for rtype in (StatusResult, ):
|
||||||
app.ctx.formatters[rtype] = formatting.create(rtype)
|
app.ctx.formatters[rtype] = formatting.create(rtype)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"""
|
"""
|
||||||
Server implementation using the starlette webserver framework.
|
Server implementation using the starlette webserver framework.
|
||||||
"""
|
"""
|
||||||
from typing import Any, Type
|
from typing import Any, Type, Optional, Mapping
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from starlette.applications import Starlette
|
from starlette.applications import Starlette
|
||||||
@@ -67,11 +67,12 @@ V1_ROUTES = [
|
|||||||
Route('/status', endpoint=on_status)
|
Route('/status', endpoint=on_status)
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_application(project_dir: Path) -> Starlette:
|
def get_application(project_dir: Path,
|
||||||
|
environ: Optional[Mapping[str, str]] = None) -> Starlette:
|
||||||
""" Create a Nominatim falcon ASGI application.
|
""" Create a Nominatim falcon ASGI application.
|
||||||
"""
|
"""
|
||||||
app = Starlette(debug=True, routes=V1_ROUTES)
|
app = Starlette(debug=True, routes=V1_ROUTES)
|
||||||
|
|
||||||
app.state.API = NominatimAPIAsync(project_dir)
|
app.state.API = NominatimAPIAsync(project_dir, environ)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ userconfig = {
|
|||||||
'SERVER_MODULE_PATH' : None,
|
'SERVER_MODULE_PATH' : None,
|
||||||
'TOKENIZER' : None, # Test with a custom tokenizer
|
'TOKENIZER' : None, # Test with a custom tokenizer
|
||||||
'STYLE' : 'extratags',
|
'STYLE' : 'extratags',
|
||||||
|
'API_ENGINE': 'php',
|
||||||
'PHPCOV' : False, # set to output directory to enable code coverage
|
'PHPCOV' : False, # set to output directory to enable code coverage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,13 @@
|
|||||||
# Copyright (C) 2022 by the Nominatim developer community.
|
# Copyright (C) 2022 by the Nominatim developer community.
|
||||||
# For a full list of authors see the git log.
|
# For a full list of authors see the git log.
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
from asgi_lifespan import LifespanManager
|
||||||
|
import httpx
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
|
|
||||||
@@ -49,6 +53,12 @@ class NominatimEnvironment:
|
|||||||
self.api_db_done = False
|
self.api_db_done = False
|
||||||
self.website_dir = None
|
self.website_dir = None
|
||||||
|
|
||||||
|
self.api_engine = None
|
||||||
|
if config['API_ENGINE'] != 'php':
|
||||||
|
if not hasattr(self, f"create_api_request_func_{config['API_ENGINE']}"):
|
||||||
|
raise RuntimeError(f"Unknown API engine '{config['API_ENGINE']}'")
|
||||||
|
self.api_engine = getattr(self, f"create_api_request_func_{config['API_ENGINE']}")()
|
||||||
|
|
||||||
def connect_database(self, dbname):
|
def connect_database(self, dbname):
|
||||||
""" Return a connection to the database with the given name.
|
""" Return a connection to the database with the given name.
|
||||||
Uses configured host, user and port.
|
Uses configured host, user and port.
|
||||||
@@ -323,3 +333,49 @@ class NominatimEnvironment:
|
|||||||
WHERE class='place' and type='houses'
|
WHERE class='place' and type='houses'
|
||||||
and osm_type='W'
|
and osm_type='W'
|
||||||
and ST_GeometryType(geometry) = 'ST_LineString'""")
|
and ST_GeometryType(geometry) = 'ST_LineString'""")
|
||||||
|
|
||||||
|
|
||||||
|
def create_api_request_func_starlette(self):
|
||||||
|
import nominatim.server.starlette.server
|
||||||
|
|
||||||
|
async def _request(endpoint, params, project_dir, environ):
|
||||||
|
app = nominatim.server.starlette.server.get_application(project_dir, environ)
|
||||||
|
|
||||||
|
async with LifespanManager(app):
|
||||||
|
async with httpx.AsyncClient(app=app, base_url="http://nominatim.test") as client:
|
||||||
|
response = await client.get(f"/{endpoint}", params=params)
|
||||||
|
|
||||||
|
return response.text, response.status_code
|
||||||
|
|
||||||
|
return _request
|
||||||
|
|
||||||
|
|
||||||
|
def create_api_request_func_sanic(self):
|
||||||
|
import nominatim.server.sanic.server
|
||||||
|
|
||||||
|
async def _request(endpoint, params, project_dir, environ):
|
||||||
|
app = nominatim.server.sanic.server.get_application(project_dir, environ)
|
||||||
|
|
||||||
|
_, response = await app.asgi_client.get(f"/{endpoint}", params=params)
|
||||||
|
|
||||||
|
return response.text, response.status_code
|
||||||
|
|
||||||
|
return _request
|
||||||
|
|
||||||
|
|
||||||
|
def create_api_request_func_falcon(self):
|
||||||
|
import nominatim.server.falcon.server
|
||||||
|
import falcon.testing
|
||||||
|
|
||||||
|
async def _request(endpoint, params, project_dir, environ):
|
||||||
|
app = nominatim.server.falcon.server.get_application(project_dir, environ)
|
||||||
|
|
||||||
|
async with falcon.testing.ASGIConductor(app) as conductor:
|
||||||
|
response = await conductor.get(f"/{endpoint}", params=params)
|
||||||
|
|
||||||
|
return response.text, response.status_code
|
||||||
|
|
||||||
|
return _request
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
Queries may either be run directly via PHP using the query script
|
Queries may either be run directly via PHP using the query script
|
||||||
or via the HTTP interface using php-cgi.
|
or via the HTTP interface using php-cgi.
|
||||||
"""
|
"""
|
||||||
|
from pathlib import Path
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import asyncio
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from utils import run_script
|
from utils import run_script
|
||||||
@@ -72,6 +74,16 @@ def send_api_query(endpoint, params, fmt, context):
|
|||||||
for h in context.table.headings:
|
for h in context.table.headings:
|
||||||
params[h] = context.table[0][h]
|
params[h] = context.table[0][h]
|
||||||
|
|
||||||
|
if context.nominatim.api_engine is None:
|
||||||
|
return send_api_query_php(endpoint, params, context)
|
||||||
|
|
||||||
|
return asyncio.run(context.nominatim.api_engine(endpoint, params,
|
||||||
|
Path(context.nominatim.website_dir.name),
|
||||||
|
context.nominatim.test_env))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def send_api_query_php(endpoint, params, context):
|
||||||
env = dict(BASE_SERVER_ENV)
|
env = dict(BASE_SERVER_ENV)
|
||||||
env['QUERY_STRING'] = urlencode(params)
|
env['QUERY_STRING'] = urlencode(params)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user