Fire insert-rows on /db/-/create if rows were inserted, refs #2260

pull/2096/merge
Simon Willison 2024-02-16 13:58:33 -08:00
rodzic 244f3ff83a
commit 3a999a85fb
2 zmienionych plików z 69 dodań i 15 usunięć

Wyświetl plik

@ -10,7 +10,7 @@ import re
import sqlite_utils
import textwrap
from datasette.events import AlterTableEvent, CreateTableEvent
from datasette.events import AlterTableEvent, CreateTableEvent, InsertRowsEvent
from datasette.database import QueryInterrupted
from datasette.utils import (
add_cors_headers,
@ -1022,6 +1022,17 @@ class TableCreateView(BaseView):
request.actor, database=db.name, table=table_name, schema=schema
)
)
if rows:
await self.ds.track_event(
InsertRowsEvent(
request.actor,
database=db.name,
table=table_name,
num_rows=len(rows),
ignore=ignore,
replace=replace,
)
)
return Response.json(details, status=201)

Wyświetl plik

@ -857,13 +857,14 @@ async def test_drop_table(ds_write, scenario):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"input,expected_status,expected_response",
"input,expected_status,expected_response,expected_events",
(
# Permission error with a bad token
(
{"table": "bad", "row": {"id": 1}},
403,
{"ok": False, "errors": ["Permission denied"]},
[],
),
# Successful creation with columns:
(
@ -910,6 +911,7 @@ async def test_drop_table(ds_write, scenario):
")"
),
},
["create-table"],
),
# Successful creation with rows:
(
@ -945,6 +947,7 @@ async def test_drop_table(ds_write, scenario):
),
"row_count": 2,
},
["create-table", "insert-rows"],
),
# Successful creation with row:
(
@ -973,6 +976,7 @@ async def test_drop_table(ds_write, scenario):
),
"row_count": 1,
},
["create-table", "insert-rows"],
),
# Create with row and no primary key
(
@ -992,6 +996,7 @@ async def test_drop_table(ds_write, scenario):
"schema": ("CREATE TABLE [four] (\n" " [name] TEXT\n" ")"),
"row_count": 1,
},
["create-table", "insert-rows"],
),
# Create table with compound primary key
(
@ -1013,6 +1018,7 @@ async def test_drop_table(ds_write, scenario):
),
"row_count": 1,
},
["create-table", "insert-rows"],
),
# Error: Table is required
(
@ -1024,6 +1030,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Table is required"],
},
[],
),
# Error: Invalid table name
(
@ -1036,6 +1043,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Invalid table name"],
},
[],
),
# Error: JSON must be an object
(
@ -1045,6 +1053,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["JSON must be an object"],
},
[],
),
# Error: Cannot specify columns with rows or row
(
@ -1058,6 +1067,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Cannot specify columns with rows or row"],
},
[],
),
# Error: columns, rows or row is required
(
@ -1069,6 +1079,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["columns, rows or row is required"],
},
[],
),
# Error: columns must be a list
(
@ -1081,6 +1092,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["columns must be a list"],
},
[],
),
# Error: columns must be a list of objects
(
@ -1093,6 +1105,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["columns must be a list of objects"],
},
[],
),
# Error: Column name is required
(
@ -1105,6 +1118,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Column name is required"],
},
[],
),
# Error: Unsupported column type
(
@ -1117,6 +1131,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Unsupported column type: bad"],
},
[],
),
# Error: Duplicate column name
(
@ -1132,6 +1147,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Duplicate column name: id"],
},
[],
),
# Error: rows must be a list
(
@ -1144,6 +1160,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["rows must be a list"],
},
[],
),
# Error: rows must be a list of objects
(
@ -1156,6 +1173,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["rows must be a list of objects"],
},
[],
),
# Error: pk must be a string
(
@ -1169,6 +1187,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["pk must be a string"],
},
[],
),
# Error: Cannot specify both pk and pks
(
@ -1183,6 +1202,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["Cannot specify both pk and pks"],
},
[],
),
# Error: pks must be a list
(
@ -1196,12 +1216,14 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["pks must be a list"],
},
[],
),
# Error: pks must be a list of strings
(
{"table": "bad", "row": {"id": 1, "name": "Row 1"}, "pks": [1, 2]},
400,
{"ok": False, "errors": ["pks must be a list of strings"]},
[],
),
# Error: ignore and replace are mutually exclusive
(
@ -1217,6 +1239,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["ignore and replace are mutually exclusive"],
},
[],
),
# ignore and replace require row or rows
(
@ -1230,6 +1253,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["ignore and replace require row or rows"],
},
[],
),
# ignore and replace require pk or pks
(
@ -1243,6 +1267,7 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["ignore and replace require pk or pks"],
},
[],
),
(
{
@ -1255,10 +1280,14 @@ async def test_drop_table(ds_write, scenario):
"ok": False,
"errors": ["ignore and replace require pk or pks"],
},
[],
),
),
)
async def test_create_table(ds_write, input, expected_status, expected_response):
async def test_create_table(
ds_write, input, expected_status, expected_response, expected_events
):
ds_write._tracked_events = []
# Special case for expected status of 403
if expected_status == 403:
token = "bad_token"
@ -1272,12 +1301,9 @@ async def test_create_table(ds_write, input, expected_status, expected_response)
assert response.status_code == expected_status
data = response.json()
assert data == expected_response
# create-table event
if expected_status == 201:
event = last_event(ds_write)
assert event.name == "create-table"
assert event.actor == {"id": "root", "token": "dstok"}
assert event.schema.startswith("CREATE TABLE ")
# Should have tracked the expected events
events = ds_write._tracked_events
assert [e.name for e in events] == expected_events
@pytest.mark.asyncio
@ -1376,6 +1402,8 @@ async def test_create_table_ignore_replace(ds_write, input, expected_rows_after)
)
assert first_response.status_code == 201
ds_write._tracked_events = []
# Try a second time
second_response = await ds_write.client.post(
"/data/-/create",
@ -1387,6 +1415,10 @@ async def test_create_table_ignore_replace(ds_write, input, expected_rows_after)
rows = await ds_write.client.get("/data/test_insert_replace.json?_shape=array")
assert rows.json() == expected_rows_after
# Check it fired the right events
event_names = [e.name for e in ds_write._tracked_events]
assert event_names == ["insert-rows"]
@pytest.mark.asyncio
async def test_create_table_error_if_pk_changed(ds_write):
@ -1471,6 +1503,7 @@ async def test_method_not_allowed(ds_write, path):
@pytest.mark.asyncio
async def test_create_uses_alter_by_default_for_new_table(ds_write):
ds_write._tracked_events = []
token = write_token(ds_write)
response = await ds_write.client.post(
"/data/-/create",
@ -1490,8 +1523,8 @@ async def test_create_uses_alter_by_default_for_new_table(ds_write):
headers=_headers(token),
)
assert response.status_code == 201
event = last_event(ds_write)
assert event.name == "create-table"
event_names = [e.name for e in ds_write._tracked_events]
assert event_names == ["create-table", "insert-rows"]
@pytest.mark.asyncio
@ -1517,6 +1550,8 @@ async def test_create_using_alter_against_existing_table(
headers=_headers(token),
)
assert response.status_code == 201
ds_write._tracked_events = []
# Now try to insert more rows using /-/create with alter=True
response2 = await ds_write.client.post(
"/data/-/create",
@ -1536,8 +1571,16 @@ async def test_create_using_alter_against_existing_table(
}
else:
assert response2.status_code == 201
event_names = [e.name for e in ds_write._tracked_events]
assert event_names == ["alter-table", "insert-rows"]
# It should have altered the table
event = last_event(ds_write)
assert event.name == "alter-table"
assert "extra" not in event.before_schema
assert "extra" in event.after_schema
alter_event = ds_write._tracked_events[0]
assert alter_event.name == "alter-table"
assert "extra" not in alter_event.before_schema
assert "extra" in alter_event.after_schema
insert_rows_event = ds_write._tracked_events[1]
assert insert_rows_event.name == "insert-rows"
assert insert_rows_event.num_rows == 1