kopia lustrzana https://github.com/simonw/datasette
actors_from_ids plugin hook and datasette.actors_from_ids() method (#2181)
* Prototype of actors_from_ids plugin hook, refs #2180 * datasette-remote-actors example plugin, refs #2180pull/2182/head
rodzic
c26370485a
commit
b645174271
|
@ -819,6 +819,16 @@ class Datasette:
|
|||
)
|
||||
return crumbs
|
||||
|
||||
async def actors_from_ids(
|
||||
self, actor_ids: Iterable[Union[str, int]]
|
||||
) -> Dict[Union[id, str], Dict]:
|
||||
result = pm.hook.actors_from_ids(datasette=self, actor_ids=actor_ids)
|
||||
if result is None:
|
||||
# Do the default thing
|
||||
return {actor_id: {"id": actor_id} for actor_id in actor_ids}
|
||||
result = await await_me_maybe(result)
|
||||
return result
|
||||
|
||||
async def permission_allowed(
|
||||
self, actor, action, resource=None, default=DEFAULT_NOT_SET
|
||||
):
|
||||
|
|
|
@ -94,6 +94,11 @@ def actor_from_request(datasette, request):
|
|||
"""Return an actor dictionary based on the incoming request"""
|
||||
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def actors_from_ids(datasette, actor_ids):
|
||||
"""Returns a dictionary mapping those IDs to actor dictionaries"""
|
||||
|
||||
|
||||
@hookspec
|
||||
def filters_from_request(request, database, table, datasette):
|
||||
"""
|
||||
|
|
|
@ -322,6 +322,27 @@ await .render_template(template, context=None, request=None)
|
|||
|
||||
Renders a `Jinja template <https://jinja.palletsprojects.com/en/2.11.x/>`__ using Datasette's preconfigured instance of Jinja and returns the resulting string. The template will have access to Datasette's default template functions and any functions that have been made available by other plugins.
|
||||
|
||||
.. _datasette_actors_from_ids:
|
||||
|
||||
await .actors_from_ids(actor_ids)
|
||||
---------------------------------
|
||||
|
||||
``actor_ids`` - list of strings or integers
|
||||
A list of actor IDs to look up.
|
||||
|
||||
Returns a dictionary, where the keys are the IDs passed to it and the values are the corresponding actor dictionaries.
|
||||
|
||||
This method is mainly designed to be used with plugins. See the :ref:`plugin_hook_actors_from_ids` documentation for details.
|
||||
|
||||
If no plugins that implement that hook are installed, the default return value looks like this:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"1": {"id": "1"},
|
||||
"2": {"id": "2"}
|
||||
}
|
||||
|
||||
.. _datasette_permission_allowed:
|
||||
|
||||
await .permission_allowed(actor, action, resource=None, default=...)
|
||||
|
|
|
@ -1071,6 +1071,63 @@ Instead of returning a dictionary, this function can return an awaitable functio
|
|||
|
||||
Examples: `datasette-auth-tokens <https://datasette.io/plugins/datasette-auth-tokens>`_, `datasette-auth-passwords <https://datasette.io/plugins/datasette-auth-passwords>`_
|
||||
|
||||
.. _plugin_hook_actors_from_ids:
|
||||
|
||||
actors_from_ids(datasette, actor_ids)
|
||||
-------------------------------------
|
||||
|
||||
``datasette`` - :ref:`internals_datasette`
|
||||
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``, or to execute SQL queries.
|
||||
|
||||
``actor_ids`` - list of strings or integers
|
||||
The actor IDs to look up.
|
||||
|
||||
The hook must return a dictionary that maps the incoming actor IDs to their full dictionary representation.
|
||||
|
||||
Some plugins that implement social features may store the ID of the :ref:`actor <authentication_actor>` that performed an action - added a comment, bookmarked a table or similar - and then need a way to resolve those IDs into display-friendly actor dictionaries later on.
|
||||
|
||||
Unlike other plugin hooks, this only uses the first implementation of the hook to return a result. You can expect users to only have a single plugin installed that implements this hook.
|
||||
|
||||
If no plugin is installed, Datasette defaults to returning actors that are just ``{"id": actor_id}``.
|
||||
|
||||
The hook can return a dictionary or an awaitable function that then returns a dictionary.
|
||||
|
||||
This example implementation returns actors from a database table:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def actors_from_ids(datasette, actor_ids):
|
||||
db = datasette.get_database("actors")
|
||||
|
||||
async def inner():
|
||||
sql = "select id, name from actors where id in ({})".format(
|
||||
", ".join("?" for _ in actor_ids)
|
||||
)
|
||||
actors = {}
|
||||
for row in (await db.execute(sql, actor_ids)).rows:
|
||||
actor = dict(row)
|
||||
actors[actor["id"]] = actor
|
||||
return actors
|
||||
|
||||
return inner
|
||||
|
||||
The returned dictionary from this example looks like this:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"1": {"id": "1", "name": "Tony"},
|
||||
"2": {"id": "2", "name": "Tina"},
|
||||
}
|
||||
|
||||
These IDs could be integers or strings, depending on how the actors used by the Datasette instance are configured.
|
||||
|
||||
Example: `datasette-remote-actors <https://github.com/datasette/datasette-remote-actors>`_
|
||||
|
||||
.. _plugin_hook_filters_from_request:
|
||||
|
||||
filters_from_request(request, database, table, datasette)
|
||||
|
|
|
@ -1215,3 +1215,65 @@ async def test_hook_register_permissions_allows_identical_duplicates():
|
|||
await ds.invoke_startup()
|
||||
# Check that ds.permissions has only one of each
|
||||
assert len([p for p in ds.permissions.values() if p.abbr == "abbr1"]) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hook_actors_from_ids():
|
||||
# Without the hook should return default {"id": id} list
|
||||
ds = Datasette()
|
||||
await ds.invoke_startup()
|
||||
db = ds.add_memory_database("actors_from_ids")
|
||||
await db.execute_write(
|
||||
"create table actors (id text primary key, name text, age int)"
|
||||
)
|
||||
await db.execute_write(
|
||||
"insert into actors (id, name, age) values ('3', 'Cate Blanchett', 52)"
|
||||
)
|
||||
await db.execute_write(
|
||||
"insert into actors (id, name, age) values ('5', 'Rooney Mara', 36)"
|
||||
)
|
||||
await db.execute_write(
|
||||
"insert into actors (id, name, age) values ('7', 'Sarah Paulson', 46)"
|
||||
)
|
||||
await db.execute_write(
|
||||
"insert into actors (id, name, age) values ('9', 'Helena Bonham Carter', 55)"
|
||||
)
|
||||
table_names = await db.table_names()
|
||||
assert table_names == ["actors"]
|
||||
actors1 = await ds.actors_from_ids(["3", "5", "7"])
|
||||
assert actors1 == {
|
||||
"3": {"id": "3"},
|
||||
"5": {"id": "5"},
|
||||
"7": {"id": "7"},
|
||||
}
|
||||
|
||||
class ActorsFromIdsPlugin:
|
||||
__name__ = "ActorsFromIdsPlugin"
|
||||
|
||||
@hookimpl
|
||||
def actors_from_ids(self, datasette, actor_ids):
|
||||
db = datasette.get_database("actors_from_ids")
|
||||
|
||||
async def inner():
|
||||
sql = "select id, name from actors where id in ({})".format(
|
||||
", ".join("?" for _ in actor_ids)
|
||||
)
|
||||
actors = {}
|
||||
result = await db.execute(sql, actor_ids)
|
||||
for row in result.rows:
|
||||
actor = dict(row)
|
||||
actors[actor["id"]] = actor
|
||||
return actors
|
||||
|
||||
return inner
|
||||
|
||||
try:
|
||||
pm.register(ActorsFromIdsPlugin(), name="ActorsFromIdsPlugin")
|
||||
actors2 = await ds.actors_from_ids(["3", "5", "7"])
|
||||
assert actors2 == {
|
||||
"3": {"id": "3", "name": "Cate Blanchett"},
|
||||
"5": {"id": "5", "name": "Rooney Mara"},
|
||||
"7": {"id": "7", "name": "Sarah Paulson"},
|
||||
}
|
||||
finally:
|
||||
pm.unregister(name="ReturnNothingPlugin")
|
||||
|
|
Ładowanie…
Reference in New Issue