{"html_url": "https://github.com/simonw/datasette/issues/647#issuecomment-1061267615", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/647", "id": 1061267615, "node_id": "IC_kwDOBm6k_c4_Qaif", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T00:05:43Z", "updated_at": "2022-03-08T00:05:43Z", "author_association": "OWNER", "body": "Built a prototype of that plugin:\r\n```python\r\nfrom datasette import hookimpl\r\nfrom functools import wraps\r\n\r\n\r\n@hookimpl\r\ndef asgi_wrapper(datasette):\r\n def wrap_with_hashed_urls(app):\r\n @wraps(app)\r\n async def hashed_urls(scope, receive, send):\r\n # Only triggers on pages with a path not starting in /-/\r\n # and where the first page component matches a database name\r\n if scope.get(\"type\") != \"http\":\r\n await app(scope, receive, send)\r\n return\r\n path = scope[\"path\"].lstrip(\"/\")\r\n if not path or path.startswith(\"-/\"):\r\n await app(scope, receive, send)\r\n return\r\n potential_database = path.split(\"/\")[0]\r\n # It may or may not be already dbname~hash\r\n if \"~\" in potential_database:\r\n db_name, hash = potential_database.split(\"~\", 1)\r\n else:\r\n db_name = potential_database\r\n hash = \"\"\r\n # Is db_name a database we have a hash for?\r\n try:\r\n db = datasette.get_database(db_name)\r\n except KeyError:\r\n await app(scope, receive, send)\r\n return\r\n\r\n if db.hash is not None:\r\n # TODO: make sure db.hash is documented\r\n if db.hash[:7] != hash:\r\n # Send a redirect\r\n path_bits = path.split(\"/\")\r\n new_path = \"/\" + \"/\".join([\"{}-{}\".format(db_name, db.hash[:7])] + path_bits[1:])\r\n if scope.get(\"query_string\"):\r\n new_path += \"?\" + scope[\"query_string\"].decode(\"latin-1\")\r\n\r\n await send({\r\n \"type\": \"http.response.start\",\r\n \"status\": 302,\r\n \"headers\": [\r\n [b\"location\", new_path.encode(\"latin1\")]\r\n ],\r\n })\r\n await send({\"type\": \"http.response.body\", \"body\": b\"\"})\r\n return\r\n else:\r\n # Add a far-future cache header\r\n async def wrapped_send(event):\r\n if event[\"type\"] == \"http.response.start\":\r\n original_headers = event.get(\"headers\") or []\r\n event = {\r\n \"type\": event[\"type\"],\r\n \"status\": event[\"status\"],\r\n \"headers\": original_headers + [\r\n [b\"Cache-Control\", b\"max-age=31536000\"]\r\n ],\r\n }\r\n await send(event)\r\n\r\n await app(scope, receive, wrapped_send)\r\n return\r\n \r\n await app(scope, receive, send)\r\n return hashed_urls\r\n return wrap_with_hashed_urls\r\n```\r\nOne catch: it doesn't affect the way URLs are generated - so every internal link within Datasette links to the non-hash version and then triggers a 302 redirect to the hashed version.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 531755959, "label": "Move hashed URL mode out to a plugin"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/647#issuecomment-1061272544", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/647", "id": 1061272544, "node_id": "IC_kwDOBm6k_c4_Qbvg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T00:14:42Z", "updated_at": "2022-03-08T00:14:42Z", "author_association": "OWNER", "body": "Maybe the plugin should interfere with `datasette.databases` on startup and change the registered name for each one?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 531755959, "label": "Move hashed URL mode out to a plugin"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/647#issuecomment-1061276399", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/647", "id": 1061276399, "node_id": "IC_kwDOBm6k_c4_Qcrv", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T00:21:47Z", "updated_at": "2022-03-08T00:21:47Z", "author_association": "OWNER", "body": "This seems to do the job:\r\n```python\r\n@hookimpl\r\ndef startup(datasette):\r\n for name, database in datasette.databases.items():\r\n if database.hash:\r\n new_name = \"{}_{}\".format(name, database.hash[:7])\r\n del datasette.databases[name]\r\n datasette.databases[new_name] = database\r\n```\r\nWould have to teach the rest of the plugin to split on `_` and to only redirect if the user seems to be hitting the URL for an old hash after which Datasette has been restarted with an updated database.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 531755959, "label": "Move hashed URL mode out to a plugin"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/647#issuecomment-1061276646", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/647", "id": 1061276646, "node_id": "IC_kwDOBm6k_c4_Qcvm", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T00:22:11Z", "updated_at": "2022-03-08T00:22:11Z", "author_association": "OWNER", "body": "I'm now convinced this is feasible enough that it's worth doing in time for Datasette 1.0.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 531755959, "label": "Move hashed URL mode out to a plugin"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/647#issuecomment-1061282743", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/647", "id": 1061282743, "node_id": "IC_kwDOBm6k_c4_QeO3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T00:32:34Z", "updated_at": "2022-03-08T00:32:47Z", "author_association": "OWNER", "body": "It would be neat if the plugin could spot old-style hyphen hash URLs (maybe on 404) and redirect those too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 531755959, "label": "Move hashed URL mode out to a plugin"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1645#issuecomment-1061355871", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1645", "id": 1061355871, "node_id": "IC_kwDOBm6k_c4_QwFf", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T02:59:28Z", "updated_at": "2022-03-08T02:59:28Z", "author_association": "OWNER", "body": "Hah, found a TODO about this: https://github.com/simonw/datasette/blob/c5791156d92615f25696ba93dae5bb2dcc192c98/datasette/app.py#L997-L999", "reactions": "{\"total_count\": 1, \"+1\": 0, \"-1\": 0, \"laugh\": 1, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1154399841, "label": "Sensible `cache-control` headers for static assets, including those served by plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1651#issuecomment-1061359915", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1651", "id": 1061359915, "node_id": "IC_kwDOBm6k_c4_QxEr", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T03:08:14Z", "updated_at": "2022-03-08T03:09:24Z", "author_association": "OWNER", "body": "A lot of the code complexity here is caused by `DataView` ([here](https://github.com/simonw/datasette/blob/c5791156d92615f25696ba93dae5bb2dcc192c98/datasette/views/base.py#L182-L669)), which has the logic for CSV streaming and plugin formats such that it can be shared between tables and custom queries.\r\n\r\nIt would be good to get rid of that subclassed shared code, figure out how to do it via a utility function instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1161584460, "label": "Get rid of the no-longer necessary ?_format=json hack for tables called x.json"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/932#issuecomment-1061891851", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/932", "id": 1061891851, "node_id": "IC_kwDOBm6k_c4_Sy8L", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-08T15:20:48Z", "updated_at": "2022-03-08T15:20:48Z", "author_association": "OWNER", "body": "Made a start on this here: https://datasette.io/tutorials ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 678760988, "label": "End-user documentation"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1384#issuecomment-1062124485", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1384", "id": 1062124485, "node_id": "IC_kwDOBm6k_c4_TrvF", "user": {"value": 167160, "label": "khusmann"}, "created_at": "2022-03-08T19:26:32Z", "updated_at": "2022-03-08T19:26:32Z", "author_association": "NONE", "body": "Looks like I'm late to the party here, but wanted to join the convo if there's still time before this interface is solidified in v1.0. My plugin use case is for education / social science data, which is meta-data heavy in the documentation of measurement scales, instruments, collection procedures, etc. that I want to connect to columns, tables, and dbs (and render in static pages, but looks like I can do that with the jinja plugin hook). I'm still digging in and I think @brandonrobertz 's approach will work for me at least for now, but I want to bump this thread in the meantime -- are there still plans for an async metadata hook at some point in the future? (or are you considering other directions?)", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 930807135, "label": "Plugin hook for dynamic metadata"}, "performed_via_github_app": null}