datasette.client now applies base_url, closes #1026

This commit is contained in:
Simon Willison 2020-10-31 12:29:42 -07:00
commit 84bc7244c1
5 changed files with 56 additions and 13 deletions

View file

@ -44,6 +44,7 @@ from .url_builder import Urls
from .database import Database, QueryInterrupted from .database import Database, QueryInterrupted
from .utils import ( from .utils import (
PrefixedUrlString,
async_call_with_supported_arguments, async_call_with_supported_arguments,
await_me_maybe, await_me_maybe,
call_with_supported_arguments, call_with_supported_arguments,
@ -1242,9 +1243,12 @@ class NotFoundExplicit(NotFound):
class DatasetteClient: class DatasetteClient:
def __init__(self, ds): def __init__(self, ds):
self.ds = ds
self.app = ds.app() self.app = ds.app()
def _fix(self, path): def _fix(self, path):
if not isinstance(path, PrefixedUrlString):
path = self.ds.urls.path(path)
if path.startswith("/"): if path.startswith("/"):
path = "http://localhost{}".format(path) path = "http://localhost{}".format(path)
return path return path

View file

@ -387,9 +387,9 @@ class Response:
) )
@classmethod @classmethod
def json(cls, body, status=200, headers=None): def json(cls, body, status=200, headers=None, default=None):
return cls( return cls(
json.dumps(body), json.dumps(body, default=default),
status=status, status=status,
headers=headers, headers=headers,
content_type="application/json; charset=utf-8", content_type="application/json; charset=utf-8",

View file

@ -387,6 +387,18 @@ It offers the following methods:
``await datasette.client.request(method, path, **kwargs)`` - returns HTTPX Response ``await datasette.client.request(method, path, **kwargs)`` - returns HTTPX Response
Execute an internal request with the given HTTP method against that path. Execute an internal request with the given HTTP method against that path.
These methods can be used with :ref:`internals_datasette_urls` - for example:
.. code-block:: python
table_json = (
await datasette.client.get(
datasette.urls.table("fixtures", "facetable", format="json")
)
).json()
``datasette.client`` methods automatically take the current :ref:`config_base_url` setting into account, whether or not you use the ``datasette.urls`` family of methods to construct the path.
For documentation on available ``**kwargs`` options and the shape of the HTTPX Response object refer to the `HTTPX Async documentation <https://www.python-httpx.org/async/>`__. For documentation on available ``**kwargs`` options and the shape of the HTTPX Response object refer to the `HTTPX Async documentation <https://www.python-httpx.org/async/>`__.
.. _internals_datasette_urls: .. _internals_datasette_urls:

View file

@ -257,6 +257,9 @@ def register_routes():
) )
) )
def asgi_scope(scope):
return Response.json(scope, default=repr)
return [ return [
(r"/one/$", one), (r"/one/$", one),
(r"/two/(?P<name>.*)$", two), (r"/two/(?P<name>.*)$", two),
@ -267,6 +270,7 @@ def register_routes():
(r"/not-async/$", not_async), (r"/not-async/$", not_async),
(r"/add-message/$", add_message), (r"/add-message/$", add_message),
(r"/render-message/$", render_message), (r"/render-message/$", render_message),
(r"/asgi-scope$", asgi_scope),
] ]

View file

@ -31,14 +31,37 @@ async def test_client_methods(datasette, method, path, expected_status):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_client_post(datasette): @pytest.mark.parametrize("prefix", [None, "/prefix/"])
response = await datasette.client.post( async def test_client_post(datasette, prefix):
"/-/messages", original_base_url = datasette._config["base_url"]
data={ try:
"message": "A message", if prefix is not None:
}, datasette._config["base_url"] = prefix
allow_redirects=False, response = await datasette.client.post(
) "/-/messages",
assert isinstance(response, httpx.Response) data={
assert response.status_code == 302 "message": "A message",
assert "ds_messages" in response.cookies },
allow_redirects=False,
)
assert isinstance(response, httpx.Response)
assert response.status_code == 302
assert "ds_messages" in response.cookies
finally:
datasette._config["base_url"] = original_base_url
@pytest.mark.asyncio
@pytest.mark.parametrize(
"prefix,expected_path", [(None, "/asgi-scope"), ("/prefix/", "/prefix/asgi-scope")]
)
async def test_client_path(datasette, prefix, expected_path):
original_base_url = datasette._config["base_url"]
try:
if prefix is not None:
datasette._config["base_url"] = prefix
response = await datasette.client.get("/asgi-scope")
path = response.json()["path"]
assert path == expected_path
finally:
datasette._config["base_url"] = original_base_url