kopia lustrzana https://github.com/simonw/datasette
Template slot family of plugin hooks - top_homepage() and others
New plugin hooks: top_homepage top_database top_table top_row top_query top_canned_query New datasette.utils.make_slot_function() Closes #1191pull/2241/head
rodzic
7a5adb592a
commit
c3caf36af7
|
@ -158,3 +158,33 @@ def skip_csrf(datasette, scope):
|
||||||
@hookspec
|
@hookspec
|
||||||
def handle_exception(datasette, request, exception):
|
def handle_exception(datasette, request, exception):
|
||||||
"""Handle an uncaught exception. Can return a Response or None."""
|
"""Handle an uncaught exception. Can return a Response or None."""
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def top_homepage(datasette, request):
|
||||||
|
"""HTML to include at the top of the homepage"""
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def top_database(datasette, request, database):
|
||||||
|
"""HTML to include at the top of the database page"""
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def top_table(datasette, request, database, table):
|
||||||
|
"""HTML to include at the top of the table page"""
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def top_row(datasette, request, database, table, row):
|
||||||
|
"""HTML to include at the top of the row page"""
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def top_query(datasette, request, database, sql):
|
||||||
|
"""HTML to include at the top of the query results page"""
|
||||||
|
|
||||||
|
|
||||||
|
@hookspec
|
||||||
|
def top_canned_query(datasette, request, database, query_name):
|
||||||
|
"""HTML to include at the top of the canned query page"""
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
</details>{% endif %}
|
</details>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ top_database() }}
|
||||||
|
|
||||||
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
||||||
|
|
||||||
{% if allow_execute_sql %}
|
{% if allow_execute_sql %}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ metadata.title or "Datasette" }}{% if private %} 🔒{% endif %}</h1>
|
<h1>{{ metadata.title or "Datasette" }}{% if private %} 🔒{% endif %}</h1>
|
||||||
|
|
||||||
|
{{ top_homepage() }}
|
||||||
|
|
||||||
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
||||||
|
|
||||||
{% for database in databases %}
|
{% for database in databases %}
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if private %} 🔒{% endif %}</h1>
|
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ metadata.title or database }}{% if canned_query and not metadata.title %}: {{ canned_query }}{% endif %}{% if private %} 🔒{% endif %}</h1>
|
||||||
|
|
||||||
|
{% if canned_query %}{{ top_canned_query() }}{% else %}{{ top_query() }}{% endif %}
|
||||||
|
|
||||||
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
||||||
|
|
||||||
<form class="sql" action="{{ urls.database(database) }}{% if canned_query %}/{{ canned_query }}{% endif %}" method="{% if canned_query_write %}post{% else %}get{% endif %}">
|
<form class="sql" action="{{ urls.database(database) }}{% if canned_query %}/{{ canned_query }}{% endif %}" method="{% if canned_query_write %}post{% else %}get{% endif %}">
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ table }}: {{ ', '.join(primary_key_values) }}{% if private %} 🔒{% endif %}</h1>
|
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_color }}">{{ table }}: {{ ', '.join(primary_key_values) }}{% if private %} 🔒{% endif %}</h1>
|
||||||
|
|
||||||
|
{{ top_row() }}
|
||||||
|
|
||||||
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
||||||
|
|
||||||
<p>This data as {% for name, url in renderers.items() %}<a href="{{ url }}">{{ name }}</a>{{ ", " if not loop.last }}{% endfor %}</p>
|
<p>This data as {% for name, url in renderers.items() %}<a href="{{ url }}">{{ name }}</a>{{ ", " if not loop.last }}{% endfor %}</p>
|
||||||
|
|
|
@ -45,6 +45,8 @@
|
||||||
</details>{% endif %}
|
</details>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ top_table() }}
|
||||||
|
|
||||||
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
|
||||||
|
|
||||||
{% if metadata.get("columns") %}
|
{% if metadata.get("columns") %}
|
||||||
|
|
|
@ -1283,3 +1283,20 @@ def fail_if_plugins_in_metadata(metadata: dict, filename=None):
|
||||||
f'Datasette no longer accepts plugin configuration in --metadata. Move your "plugins" configuration blocks to a separate file - we suggest calling that datasette.{suggested_extension} - and start Datasette with datasette -c datasette.{suggested_extension}. See https://docs.datasette.io/en/latest/configuration.html for more details.'
|
f'Datasette no longer accepts plugin configuration in --metadata. Move your "plugins" configuration blocks to a separate file - we suggest calling that datasette.{suggested_extension} - and start Datasette with datasette -c datasette.{suggested_extension}. See https://docs.datasette.io/en/latest/configuration.html for more details.'
|
||||||
)
|
)
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
def make_slot_function(name, datasette, request, **kwargs):
|
||||||
|
from datasette.plugins import pm
|
||||||
|
|
||||||
|
method = getattr(pm.hook, name, None)
|
||||||
|
assert method is not None, "No hook found for {}".format(name)
|
||||||
|
|
||||||
|
async def inner():
|
||||||
|
html_bits = []
|
||||||
|
for hook in method(datasette=datasette, request=request, **kwargs):
|
||||||
|
html = await await_me_maybe(hook)
|
||||||
|
if html is not None:
|
||||||
|
html_bits.append(html)
|
||||||
|
return markupsafe.Markup("".join(html_bits))
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Callable
|
|
||||||
from urllib.parse import parse_qsl, urlencode
|
from urllib.parse import parse_qsl, urlencode
|
||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
import hashlib
|
||||||
|
@ -18,6 +17,7 @@ from datasette.utils import (
|
||||||
call_with_supported_arguments,
|
call_with_supported_arguments,
|
||||||
derive_named_parameters,
|
derive_named_parameters,
|
||||||
format_bytes,
|
format_bytes,
|
||||||
|
make_slot_function,
|
||||||
tilde_decode,
|
tilde_decode,
|
||||||
to_css_class,
|
to_css_class,
|
||||||
validate_sql_select,
|
validate_sql_select,
|
||||||
|
@ -161,6 +161,9 @@ class DatabaseView(View):
|
||||||
f"{'*' if template_name == template.name else ''}{template_name}"
|
f"{'*' if template_name == template.name else ''}{template_name}"
|
||||||
for template_name in templates
|
for template_name in templates
|
||||||
],
|
],
|
||||||
|
"top_database": make_slot_function(
|
||||||
|
"top_database", datasette, request, database=database
|
||||||
|
),
|
||||||
}
|
}
|
||||||
return Response.html(
|
return Response.html(
|
||||||
await datasette.render_template(
|
await datasette.render_template(
|
||||||
|
@ -246,6 +249,12 @@ class QueryContext:
|
||||||
"help": "List of templates that were considered for rendering this page"
|
"help": "List of templates that were considered for rendering this page"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
top_query: callable = field(
|
||||||
|
metadata={"help": "Callable to render the top_query slot"}
|
||||||
|
)
|
||||||
|
top_canned_query: callable = field(
|
||||||
|
metadata={"help": "Callable to render the top_canned_query slot"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def get_tables(datasette, request, db):
|
async def get_tables(datasette, request, db):
|
||||||
|
@ -727,6 +736,16 @@ class QueryView(View):
|
||||||
f"{'*' if template_name == template.name else ''}{template_name}"
|
f"{'*' if template_name == template.name else ''}{template_name}"
|
||||||
for template_name in templates
|
for template_name in templates
|
||||||
],
|
],
|
||||||
|
top_query=make_slot_function(
|
||||||
|
"top_query", datasette, request, database=database, sql=sql
|
||||||
|
),
|
||||||
|
top_canned_query=make_slot_function(
|
||||||
|
"top_canned_query",
|
||||||
|
datasette,
|
||||||
|
request,
|
||||||
|
database=database,
|
||||||
|
query_name=canned_query["name"] if canned_query else None,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
request=request,
|
request=request,
|
||||||
view_name="database",
|
view_name="database",
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import hashlib
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from datasette.utils import add_cors_headers, CustomJSONEncoder
|
from datasette.plugins import pm
|
||||||
|
from datasette.utils import add_cors_headers, make_slot_function, CustomJSONEncoder
|
||||||
from datasette.utils.asgi import Response
|
from datasette.utils.asgi import Response
|
||||||
from datasette.version import __version__
|
from datasette.version import __version__
|
||||||
|
|
||||||
|
from markupsafe import Markup
|
||||||
|
|
||||||
from .base import BaseView
|
from .base import BaseView
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,5 +144,8 @@ class IndexView(BaseView):
|
||||||
"private": not await self.ds.permission_allowed(
|
"private": not await self.ds.permission_allowed(
|
||||||
None, "view-instance"
|
None, "view-instance"
|
||||||
),
|
),
|
||||||
|
"top_homepage": make_slot_function(
|
||||||
|
"top_homepage", self.ds, request
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,11 +2,9 @@ from datasette.utils.asgi import NotFound, Forbidden, Response
|
||||||
from datasette.database import QueryInterrupted
|
from datasette.database import QueryInterrupted
|
||||||
from .base import DataView, BaseView, _error
|
from .base import DataView, BaseView, _error
|
||||||
from datasette.utils import (
|
from datasette.utils import (
|
||||||
tilde_decode,
|
make_slot_function,
|
||||||
urlsafe_components,
|
|
||||||
to_css_class,
|
to_css_class,
|
||||||
escape_sqlite,
|
escape_sqlite,
|
||||||
row_sql_params_pks,
|
|
||||||
)
|
)
|
||||||
import json
|
import json
|
||||||
import sqlite_utils
|
import sqlite_utils
|
||||||
|
@ -73,6 +71,14 @@ class RowView(DataView):
|
||||||
.get(database, {})
|
.get(database, {})
|
||||||
.get("tables", {})
|
.get("tables", {})
|
||||||
.get(table, {}),
|
.get(table, {}),
|
||||||
|
"top_row": make_slot_function(
|
||||||
|
"top_row",
|
||||||
|
self.ds,
|
||||||
|
request,
|
||||||
|
database=resolved.db.name,
|
||||||
|
table=resolved.table,
|
||||||
|
row=rows[0],
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
|
@ -17,6 +17,7 @@ from datasette.utils import (
|
||||||
append_querystring,
|
append_querystring,
|
||||||
compound_keys_after_sql,
|
compound_keys_after_sql,
|
||||||
format_bytes,
|
format_bytes,
|
||||||
|
make_slot_function,
|
||||||
tilde_encode,
|
tilde_encode,
|
||||||
escape_sqlite,
|
escape_sqlite,
|
||||||
filters_should_redirect,
|
filters_should_redirect,
|
||||||
|
@ -842,6 +843,13 @@ async def table_view_traced(datasette, request):
|
||||||
f"{'*' if template_name == template.name else ''}{template_name}"
|
f"{'*' if template_name == template.name else ''}{template_name}"
|
||||||
for template_name in templates
|
for template_name in templates
|
||||||
],
|
],
|
||||||
|
top_table=make_slot_function(
|
||||||
|
"top_table",
|
||||||
|
datasette,
|
||||||
|
request,
|
||||||
|
database=resolved.db.name,
|
||||||
|
table=resolved.table,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
request=request,
|
request=request,
|
||||||
view_name="table",
|
view_name="table",
|
||||||
|
|
|
@ -1641,3 +1641,122 @@ This hook is responsible for returning a dictionary corresponding to Datasette :
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
Example: `datasette-remote-metadata plugin <https://datasette.io/plugins/datasette-remote-metadata>`__
|
Example: `datasette-remote-metadata plugin <https://datasette.io/plugins/datasette-remote-metadata>`__
|
||||||
|
|
||||||
|
.. _plugin_hook_slots:
|
||||||
|
|
||||||
|
Template slots
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The following set of plugin hooks can be used to return extra HTML content that will be inserted into the corresponding page, directly below the ``<h1>`` heading.
|
||||||
|
|
||||||
|
Multiple plugins can contribute content here. The order in which it is displayed can be controlled using Pluggy's `call time order options <https://pluggy.readthedocs.io/en/stable/#call-time-order>`__.
|
||||||
|
|
||||||
|
Each of these plugin hooks can return either a string or an awaitable function that returns a string.
|
||||||
|
|
||||||
|
.. _plugin_hook_top_homepage:
|
||||||
|
|
||||||
|
top_homepage(datasette, request)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``datasette`` - :ref:`internals_datasette`
|
||||||
|
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||||
|
|
||||||
|
``request`` - :ref:`internals_request`
|
||||||
|
The current HTTP request.
|
||||||
|
|
||||||
|
Returns HTML to be displayed at the top of the Datasette homepage.
|
||||||
|
|
||||||
|
.. _plugin_hook_top_database:
|
||||||
|
|
||||||
|
top_database(datasette, request, database)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``datasette`` - :ref:`internals_datasette`
|
||||||
|
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||||
|
|
||||||
|
``request`` - :ref:`internals_request`
|
||||||
|
The current HTTP request.
|
||||||
|
|
||||||
|
``database`` - string
|
||||||
|
The name of the database.
|
||||||
|
|
||||||
|
Returns HTML to be displayed at the top of the database page.
|
||||||
|
|
||||||
|
.. _plugin_hook_top_table:
|
||||||
|
|
||||||
|
top_table(datasette, request, database, table)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``datasette`` - :ref:`internals_datasette`
|
||||||
|
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||||
|
|
||||||
|
``request`` - :ref:`internals_request`
|
||||||
|
The current HTTP request.
|
||||||
|
|
||||||
|
``database`` - string
|
||||||
|
The name of the database.
|
||||||
|
|
||||||
|
``table`` - string
|
||||||
|
The name of the table.
|
||||||
|
|
||||||
|
Returns HTML to be displayed at the top of the table page.
|
||||||
|
|
||||||
|
.. _plugin_hook_top_row:
|
||||||
|
|
||||||
|
top_row(datasette, request, database, table, row)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``datasette`` - :ref:`internals_datasette`
|
||||||
|
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||||
|
|
||||||
|
``request`` - :ref:`internals_request`
|
||||||
|
The current HTTP request.
|
||||||
|
|
||||||
|
``database`` - string
|
||||||
|
The name of the database.
|
||||||
|
|
||||||
|
``table`` - string
|
||||||
|
The name of the table.
|
||||||
|
|
||||||
|
``row`` - ``sqlite.Row``
|
||||||
|
The SQLite row object being displayed.
|
||||||
|
|
||||||
|
Returns HTML to be displayed at the top of the row page.
|
||||||
|
|
||||||
|
.. _plugin_hook_top_query:
|
||||||
|
|
||||||
|
top_query(datasette, request, database, sql)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``datasette`` - :ref:`internals_datasette`
|
||||||
|
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||||
|
|
||||||
|
``request`` - :ref:`internals_request`
|
||||||
|
The current HTTP request.
|
||||||
|
|
||||||
|
``database`` - string
|
||||||
|
The name of the database.
|
||||||
|
|
||||||
|
``sql`` - string
|
||||||
|
The SQL query.
|
||||||
|
|
||||||
|
Returns HTML to be displayed at the top of the query results page.
|
||||||
|
|
||||||
|
.. _plugin_hook_top_canned_query:
|
||||||
|
|
||||||
|
top_canned_query(datasette, request, database, query_name)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``datasette`` - :ref:`internals_datasette`
|
||||||
|
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||||
|
|
||||||
|
``request`` - :ref:`internals_request`
|
||||||
|
The current HTTP request.
|
||||||
|
|
||||||
|
``database`` - string
|
||||||
|
The name of the database.
|
||||||
|
|
||||||
|
``query_name`` - string
|
||||||
|
The name of the canned query.
|
||||||
|
|
||||||
|
Returns HTML to be displayed at the top of the canned query page.
|
||||||
|
|
|
@ -41,7 +41,9 @@ def plugin_hooks_content():
|
||||||
"plugin", [name for name in dir(app.pm.hook) if not name.startswith("_")]
|
"plugin", [name for name in dir(app.pm.hook) if not name.startswith("_")]
|
||||||
)
|
)
|
||||||
def test_plugin_hooks_are_documented(plugin, plugin_hooks_content):
|
def test_plugin_hooks_are_documented(plugin, plugin_hooks_content):
|
||||||
headings = get_headings(plugin_hooks_content, "-")
|
headings = set()
|
||||||
|
headings.update(get_headings(plugin_hooks_content, "-"))
|
||||||
|
headings.update(get_headings(plugin_hooks_content, "~"))
|
||||||
assert plugin in headings
|
assert plugin in headings
|
||||||
hook_caller = getattr(app.pm.hook, plugin)
|
hook_caller = getattr(app.pm.hook, plugin)
|
||||||
arg_names = [a for a in hook_caller.spec.argnames if a != "__multicall__"]
|
arg_names = [a for a in hook_caller.spec.argnames if a != "__multicall__"]
|
||||||
|
|
|
@ -1334,3 +1334,104 @@ async def test_hook_jinja2_environment_from_request(tmpdir):
|
||||||
assert "Hello museums!" in response2.text
|
assert "Hello museums!" in response2.text
|
||||||
finally:
|
finally:
|
||||||
pm.unregister(name="EnvironmentPlugin")
|
pm.unregister(name="EnvironmentPlugin")
|
||||||
|
|
||||||
|
|
||||||
|
class SlotPlugin:
|
||||||
|
__name__ = "SlotPlugin"
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def top_homepage(self, request):
|
||||||
|
return "Xtop_homepage:" + request.args["z"]
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def top_database(self, request, database):
|
||||||
|
async def inner():
|
||||||
|
return "Xtop_database:{}:{}".format(database, request.args["z"])
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def top_table(self, request, database, table):
|
||||||
|
return "Xtop_table:{}:{}:{}".format(database, table, request.args["z"])
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def top_row(self, request, database, table, row):
|
||||||
|
return "Xtop_row:{}:{}:{}:{}".format(
|
||||||
|
database, table, row["name"], request.args["z"]
|
||||||
|
)
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def top_query(self, request, database, sql):
|
||||||
|
return "Xtop_query:{}:{}:{}".format(database, sql, request.args["z"])
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def top_canned_query(self, request, database, query_name):
|
||||||
|
return "Xtop_query:{}:{}:{}".format(database, query_name, request.args["z"])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_hook_top_homepage():
|
||||||
|
try:
|
||||||
|
pm.register(SlotPlugin(), name="SlotPlugin")
|
||||||
|
datasette = Datasette(memory=True)
|
||||||
|
response = await datasette.client.get("/?z=foo")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Xtop_homepage:foo" in response.text
|
||||||
|
finally:
|
||||||
|
pm.unregister(name="SlotPlugin")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_hook_top_database():
|
||||||
|
try:
|
||||||
|
pm.register(SlotPlugin(), name="SlotPlugin")
|
||||||
|
datasette = Datasette(memory=True)
|
||||||
|
response = await datasette.client.get("/_memory?z=bar")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Xtop_database:_memory:bar" in response.text
|
||||||
|
finally:
|
||||||
|
pm.unregister(name="SlotPlugin")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_hook_top_table(ds_client):
|
||||||
|
try:
|
||||||
|
pm.register(SlotPlugin(), name="SlotPlugin")
|
||||||
|
response = await ds_client.get("/fixtures/facetable?z=baz")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Xtop_table:fixtures:facetable:baz" in response.text
|
||||||
|
finally:
|
||||||
|
pm.unregister(name="SlotPlugin")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_hook_top_row(ds_client):
|
||||||
|
try:
|
||||||
|
pm.register(SlotPlugin(), name="SlotPlugin")
|
||||||
|
response = await ds_client.get("/fixtures/facet_cities/1?z=bax")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Xtop_row:fixtures:facet_cities:San Francisco:bax" in response.text
|
||||||
|
finally:
|
||||||
|
pm.unregister(name="SlotPlugin")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_hook_top_query(ds_client):
|
||||||
|
try:
|
||||||
|
pm.register(SlotPlugin(), name="SlotPlugin")
|
||||||
|
response = await ds_client.get("/fixtures?sql=select+1&z=x")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Xtop_query:fixtures:select 1:x" in response.text
|
||||||
|
finally:
|
||||||
|
pm.unregister(name="SlotPlugin")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_hook_top_canned_query(ds_client):
|
||||||
|
try:
|
||||||
|
pm.register(SlotPlugin(), name="SlotPlugin")
|
||||||
|
response = await ds_client.get("/fixtures/from_hook?z=xyz")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Xtop_query:fixtures:from_hook:xyz" in response.text
|
||||||
|
finally:
|
||||||
|
pm.unregister(name="SlotPlugin")
|
||||||
|
|
Ładowanie…
Reference in New Issue