home / github

Menu
  • Search all tables
  • GraphQL API

issue_comments

Table actions
  • GraphQL API for issue_comments

10 rows where issue = 1493471221 sorted by updated_at descending

✖
✖

✎ View and edit SQL

This data as json, CSV (advanced)

Suggested facets: created_at (date), updated_at (date)

user 1

  • simonw 10

issue 1

  • `.json` errors should be returned as JSON · 10 ✖

author_association 1

  • OWNER 10
id html_url issue_url node_id user created_at updated_at ▲ author_association body reactions issue performed_via_github_app
1352411327 https://github.com/simonw/datasette/issues/1949#issuecomment-1352411327 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5QnCi_ simonw 9599 2022-12-15T00:46:27Z 2022-12-15T00:46:27Z OWNER

I got this far: ```diff diff --git a/datasette/handle_exception.py b/datasette/handle_exception.py index 8b7e83e3..31d41e00 100644 --- a/datasette/handle_exception.py +++ b/datasette/handle_exception.py @@ -54,7 +54,17 @@ def handle_exception(datasette, request, exception): headers = {} if datasette.cors: add_cors_headers(headers) - if request.path.split("?")[0].endswith(".json"): + # Return JSON error under certain conditions + should_return_json = ( + # URL ends in .json + request.path.split("?")[0].endswith(".json") + or + # Hints from incoming request headers + request.headers.get("content-type") == "application/json" + or "application/json" in request.headers.get("accept", "") + ) + breakpoint() + if should_return_json: return Response.json(info, status=status, headers=headers) else: template = datasette.jinja_env.select_template(templates) diff --git a/tests/test_api_write.py b/tests/test_api_write.py index f27d143f..982543a6 100644 --- a/tests/test_api_write.py +++ b/tests/test_api_write.py @@ -1140,6 +1140,38 @@ async def test_create_table_permissions( assert data["errors"] == expected_errors

+@pytest.mark.asyncio +@pytest.mark.parametrize( + "headers,expect_json", + ( + ({}, False), + ({"Accept": "text/html"}, True), + ({"Accept": "application/json"}, True), + ({"Content-Type": "application/json"}, True), + ({"Accept": "application/json, text/plain, /"}, True), + ({"Content-Type": "application/json"}, True), + ({"accept": "application/json, text/plain, /"}, True), + ({"content-type": "application/json"}, True), + ), +) +async def test_permission_errors_html_and_json(ds_write, headers, expect_json): + request_headers = {"Authorization": "Bearer bad_token"} + request_headers.update(headers) + response = await ds_write.client.post( + "/data/-/create", + json={}, + headers=request_headers, + ) + assert response.status_code == 403 + if expect_json: + data = response.json() + assert data["ok"] is False + assert data["errors"] == ["Permission denied"] + else: + assert response.headers["Content-Type"] == "text/html; charset=utf-8" + assert "Permission denied" in response.text + + @pytest.mark.asyncio @pytest.mark.parametrize( "input,expected_rows_after", ``` Then decided I would punt this until the next milestone.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352378370 https://github.com/simonw/datasette/issues/1949#issuecomment-1352378370 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5Qm6gC simonw 9599 2022-12-15T00:02:08Z 2022-12-15T00:04:54Z OWNER

I fixed this issue to help research this further: - https://github.com/simonw/datasette-ripgrep/issues/26

Now this search works:

https://ripgrep.datasette.io/-/ripgrep?pattern=return+_error&literal=on&glob=datasette%2F**

I wish I had this feature! - https://github.com/simonw/datasette-ripgrep/issues/24

Looks like I have both _error() and _errors() functions in there!

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352356356 https://github.com/simonw/datasette/issues/1949#issuecomment-1352356356 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5Qm1IE simonw 9599 2022-12-14T23:27:25Z 2022-12-14T23:28:16Z OWNER

Also weird: errors returned by that mechanism look like this: json { "ok": false, "errors": ["list of error messages"] } While errors returned by the rest of Datasette look like this: https://latest.datasette.io/fixtures/no_table.json

json { "ok": false, "error": "Table not found: no_table", "status": 404, "title": null } Related: - #1875

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352354927 https://github.com/simonw/datasette/issues/1949#issuecomment-1352354927 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5Qm0xv simonw 9599 2022-12-14T23:25:06Z 2022-12-14T23:25:14Z OWNER

Looks like the code I've written for permission checking on TableCreateView and friends doesn't use the regular raise Forbidden or raise DatasetteError mechanisms - it does its own thing here:

https://github.com/simonw/datasette/blob/9ad76d279e2c3874ca5070626a25458ce129f126/datasette/views/database.py#L580-L584

Which uses this:

https://github.com/simonw/datasette/blob/9ad76d279e2c3874ca5070626a25458ce129f126/datasette/views/base.py#L547-L548

Having two different patterns to return errors is bad, I should fix that.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352340518 https://github.com/simonw/datasette/issues/1949#issuecomment-1352340518 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5QmxQm simonw 9599 2022-12-14T23:07:01Z 2022-12-14T23:07:01Z OWNER

Easiest fix would be to look for accept: application/json and/or content-type: application/json headers.

Not bullet-proof, so people might occasionally make JSON requests and get back an HTML error - but the documentation can tell people that they need to send those headers if they want to reliably get back JSON error messages.

I'm happy with this as a solution.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352338620 https://github.com/simonw/datasette/issues/1949#issuecomment-1352338620 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5Qmwy8 simonw 9599 2022-12-14T23:05:17Z 2022-12-14T23:05:17Z OWNER

Sniffing for a { is a little bit tricky though, as the post body is lazily loaded on request here:

https://github.com/simonw/datasette/blob/9ad76d279e2c3874ca5070626a25458ce129f126/datasette/utils/asgi.py#L127-L135

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352335503 https://github.com/simonw/datasette/issues/1949#issuecomment-1352335503 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5QmwCP simonw 9599 2022-12-14T23:03:28Z 2022-12-14T23:03:28Z OWNER

This raises a more complicated issue

At some point I'm likely to want to add an HTML interface for creating tables and inserting and updating rows.

The obvious URLs for that are the same as for the JSON API: /db/table/-/insert and suchlike.

Those endpoints are currently POST only - and can return JSON all the time.

If they start accepting form POSTs too they'll need to be able to accept form-encoded data and return HTML instead. That's OK - they can detect incoming JSON thanks to the content-type header an the fact that the request body starts with { - but the should_return_json fix described above could intefere with how errors are returned if I'm not careful.

I think it can still work though: I'll only set should_return_json = True if the endpoint gets a POST with a body starting {, or a content-type JSON header.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352331314 https://github.com/simonw/datasette/issues/1949#issuecomment-1352331314 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5QmvAy simonw 9599 2022-12-14T22:59:36Z 2022-12-14T22:59:36Z OWNER

I'm going to prototype that up to see what it looks like.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352330825 https://github.com/simonw/datasette/issues/1949#issuecomment-1352330825 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5Qmu5J simonw 9599 2022-12-14T22:58:51Z 2022-12-14T22:59:27Z OWNER

I need a way for those JSON endpoints to communicate back to the handle_exception handler that they are returning JSON, so it knows to behave differently.

Since it gets the request object, one way could be to have view code set request.should_return_json = True so that the handler knows to do something different.

It's a bit of a cludge though!

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  
1352329027 https://github.com/simonw/datasette/issues/1949#issuecomment-1352329027 https://api.github.com/repos/simonw/datasette/issues/1949 IC_kwDOBm6k_c5QmudD simonw 9599 2022-12-14T22:56:24Z 2022-12-14T22:57:19Z OWNER

Most .json errors DO return as JSON, thanks to this:

https://github.com/simonw/datasette/blob/c094dde3ff2bae030f261e6440d4fb082eb860a9/datasette/handle_exception.py#L19-L24

https://github.com/simonw/datasette/blob/c094dde3ff2bae030f261e6440d4fb082eb860a9/datasette/handle_exception.py#L57-L58

But that code triggers when the URL ends with .json - and none of the JSON write API endpoints (things like /db/-/create and /db/table/-/insert) follow that convention.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
`.json` errors should be returned as JSON 1493471221  

Advanced export

JSON shape: default, array, newline-delimited, object

CSV options:

CREATE TABLE [issue_comments] (
   [html_url] TEXT,
   [issue_url] TEXT,
   [id] INTEGER PRIMARY KEY,
   [node_id] TEXT,
   [user] INTEGER REFERENCES [users]([id]),
   [created_at] TEXT,
   [updated_at] TEXT,
   [author_association] TEXT,
   [body] TEXT,
   [reactions] TEXT,
   [issue] INTEGER REFERENCES [issues]([id])
, [performed_via_github_app] TEXT);
CREATE INDEX [idx_issue_comments_issue]
                ON [issue_comments] ([issue]);
CREATE INDEX [idx_issue_comments_user]
                ON [issue_comments] ([user]);
Powered by Datasette · Queries took 18.465ms · About: github-to-sqlite
  • Sort ascending
  • Sort descending
  • Facet by this
  • Hide this column
  • Show all columns
  • Show not-blank rows