kopia lustrzana https://github.com/simonw/datasette
Added asgi_wrapper plugin hook, closes #520
rodzic
b9ede4c189
commit
93bfa26bfd
|
@ -651,9 +651,12 @@ class Datasette:
|
||||||
if not database.is_mutable:
|
if not database.is_mutable:
|
||||||
await database.table_counts(limit=60 * 60 * 1000)
|
await database.table_counts(limit=60 * 60 * 1000)
|
||||||
|
|
||||||
return AsgiLifespan(
|
asgi = AsgiLifespan(
|
||||||
AsgiTracer(DatasetteRouter(self, routes)), on_startup=setup_db
|
AsgiTracer(DatasetteRouter(self, routes)), on_startup=setup_db
|
||||||
)
|
)
|
||||||
|
for wrapper in pm.hook.asgi_wrapper(datasette=self):
|
||||||
|
asgi = wrapper(asgi)
|
||||||
|
return asgi
|
||||||
|
|
||||||
|
|
||||||
class DatasetteRouter(AsgiRouter):
|
class DatasetteRouter(AsgiRouter):
|
||||||
|
|
|
@ -5,6 +5,11 @@ hookspec = HookspecMarker("datasette")
|
||||||
hookimpl = HookimplMarker("datasette")
|
hookimpl = HookimplMarker("datasette")
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def asgi_wrapper(datasette):
|
||||||
|
"Returns an ASGI middleware callable to wrap our ASGI application with"
|
||||||
|
|
||||||
|
|
||||||
@hookspec
|
@hookspec
|
||||||
def prepare_connection(conn):
|
def prepare_connection(conn):
|
||||||
"Modify SQLite connection in some way e.g. register custom SQL functions"
|
"Modify SQLite connection in some way e.g. register custom SQL functions"
|
||||||
|
|
|
@ -666,3 +666,44 @@ The plugin hook can then be used to register the new facet class like this:
|
||||||
@hookimpl
|
@hookimpl
|
||||||
def register_facet_classes():
|
def register_facet_classes():
|
||||||
return [SpecialFacet]
|
return [SpecialFacet]
|
||||||
|
|
||||||
|
|
||||||
|
.. _plugin_asgi_wrapper:
|
||||||
|
|
||||||
|
asgi_wrapper(datasette)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Return an `ASGI <https://asgi.readthedocs.io/>`__ middleware wrapper function that will be applied to the Datasette ASGI application.
|
||||||
|
|
||||||
|
This is a very powerful hook. You can use it to manipulate the entire Datasette response, or even to configure new URL routes that will be handled by your own custom code.
|
||||||
|
|
||||||
|
You can write your ASGI code directly against the low-level specification, or you can use the middleware utilites provided by an ASGI framework such as `Starlette <https://www.starlette.io/middleware/>`__.
|
||||||
|
|
||||||
|
This example plugin adds a ``x-databases`` HTTP header listing the currently attached databases:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from datasette import hookimpl
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def asgi_wrapper(datasette):
|
||||||
|
def wrap_with_databases_header(app):
|
||||||
|
@wraps(app)
|
||||||
|
async def add_x_databases_header(scope, recieve, send):
|
||||||
|
async def wrapped_send(event):
|
||||||
|
if event["type"] == "http.response.start":
|
||||||
|
original_headers = event.get("headers") or []
|
||||||
|
event = {
|
||||||
|
"type": event["type"],
|
||||||
|
"status": event["status"],
|
||||||
|
"headers": original_headers + [
|
||||||
|
[b"x-databases",
|
||||||
|
", ".join(datasette.databases.keys()).encode("utf-8")]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
await send(event)
|
||||||
|
await app(scope, recieve, wrapped_send)
|
||||||
|
return add_x_databases_header
|
||||||
|
return wrap_with_databases_header
|
||||||
|
|
|
@ -372,6 +372,7 @@ def render_cell(value, column, table, database, datasette):
|
||||||
|
|
||||||
PLUGIN2 = """
|
PLUGIN2 = """
|
||||||
from datasette import hookimpl
|
from datasette import hookimpl
|
||||||
|
from functools import wraps
|
||||||
import jinja2
|
import jinja2
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -413,6 +414,28 @@ def render_cell(value, database):
|
||||||
label=jinja2.escape(data["label"] or "") or " "
|
label=jinja2.escape(data["label"] or "") or " "
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def asgi_wrapper(datasette):
|
||||||
|
def wrap_with_databases_header(app):
|
||||||
|
@wraps(app)
|
||||||
|
async def add_x_databases_header(scope, recieve, send):
|
||||||
|
async def wrapped_send(event):
|
||||||
|
if event["type"] == "http.response.start":
|
||||||
|
original_headers = event.get("headers") or []
|
||||||
|
event = {
|
||||||
|
"type": event["type"],
|
||||||
|
"status": event["status"],
|
||||||
|
"headers": original_headers + [
|
||||||
|
[b"x-databases",
|
||||||
|
", ".join(datasette.databases.keys()).encode("utf-8")]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
await send(event)
|
||||||
|
await app(scope, recieve, wrapped_send)
|
||||||
|
return add_x_databases_header
|
||||||
|
return wrap_with_databases_header
|
||||||
"""
|
"""
|
||||||
|
|
||||||
TABLES = (
|
TABLES = (
|
||||||
|
|
|
@ -162,3 +162,8 @@ def test_plugins_extra_body_script(app_client, path, expected_extra_body_script)
|
||||||
json_data = r.search(app_client.get(path).body.decode("utf8")).group(1)
|
json_data = r.search(app_client.get(path).body.decode("utf8")).group(1)
|
||||||
actual_data = json.loads(json_data)
|
actual_data = json.loads(json_data)
|
||||||
assert expected_extra_body_script == actual_data
|
assert expected_extra_body_script == actual_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugins_asgi_wrapper(app_client):
|
||||||
|
response = app_client.get("/fixtures")
|
||||||
|
assert "fixtures" == response.headers["x-databases"]
|
||||||
|
|
Ładowanie…
Reference in New Issue