Hide create-from-data UI without insert permission

Expose whether the actor can insert rows in the create-table page data, then use that flag to hide and guard the create-table-from-data path. This avoids offering a flow that fails after preview for create-table-only actors.

Refs https://github.com/simonw/datasette/pull/2813#issuecomment-4878146376
This commit is contained in:
Simon Willison 2026-07-03 09:54:51 -07:00
commit 141fe194c6
4 changed files with 48 additions and 3 deletions

View file

@ -822,6 +822,11 @@ function tableCreateSaveButtonText(state) {
return "Create table";
}
function tableCreateCanInsertRows() {
var data = databaseCreateTableData() || {};
return !!data.canInsertRows;
}
function syncTableCreateModeUi(state) {
if (!state) {
return;
@ -831,7 +836,7 @@ function syncTableCreateModeUi(state) {
state.dataPanel.hidden = !isDataMode;
state.dataEditor.hidden = !isDataMode || state.dataPreviewReady;
state.dataPreview.hidden = !isDataMode || !state.dataPreviewReady;
state.createFromDataLink.hidden = isDataMode;
state.createFromDataLink.hidden = isDataMode || !tableCreateCanInsertRows();
state.manualCreateLink.hidden = !isDataMode;
}
@ -1303,7 +1308,7 @@ function resetTableCreateDialog(state) {
}
function showTableCreateDataMode(state) {
if (!state || state.isSaving) {
if (!state || state.isSaving || !tableCreateCanInsertRows()) {
return;
}
state.mode = "data";

View file

@ -325,7 +325,7 @@ class DatabaseContext(Context):
database_color: str = field(metadata={"help": "The color assigned to the database"})
database_page_data: dict = field(
metadata={
"help": 'JSON data used by JavaScript on the database page. Currently ``{}`` or ``{"createTable": {...}}`` where ``createTable`` includes ``path``, ``foreignKeyTargetsPath``, ``databaseName``, ``columnTypes``, ``defaultExpressions`` and optional ``customColumnTypes``.'
"help": 'JSON data used by JavaScript on the database page. Currently ``{}`` or ``{"createTable": {...}}`` where ``createTable`` includes ``path``, ``foreignKeyTargetsPath``, ``databaseName``, ``columnTypes``, ``defaultExpressions``, ``canInsertRows`` and optional ``customColumnTypes``.'
}
)
database_actions: callable = field(

View file

@ -267,6 +267,11 @@ async def _create_table_ui_context(
"databaseName": database_name,
"columnTypes": CREATE_TABLE_COLUMN_TYPES,
"defaultExpressions": default_expression_options(),
"canInsertRows": await datasette.allowed(
action="insert-row",
resource=DatabaseResource(database=database_name),
actor=request.actor,
),
}
can_set_column_type = await datasette.allowed(
action="set-column-type",

View file

@ -1027,6 +1027,7 @@ async def test_database_create_table_action_button_and_data():
"databaseName": "data",
"columnTypes": ["text", "integer", "float", "blob"],
"defaultExpressions": DEFAULT_EXPRESSION_OPTIONS,
"canInsertRows": False,
},
}
assert "customColumnTypes" not in database_data_from_soup(soup)["createTable"]
@ -1050,6 +1051,40 @@ async def test_database_create_table_action_button_and_data():
ds.close()
@pytest.mark.asyncio
async def test_database_create_table_data_includes_insert_row_permission():
ds = Datasette(
[],
config={
"databases": {
"data": {
"permissions": {
"create-table": {"id": "root"},
"insert-row": {"id": "root"},
},
},
},
},
)
try:
db = ds.add_database(
Database(ds, memory_name="test_database_create_table_insert_permission"),
name="data",
)
await db.execute_write_script("""
create table items (id integer primary key, name text);
""")
response = await ds.client.get("/data", actor={"id": "root"})
assert response.status_code == 200
create_table_data = database_data_from_soup(Soup(response.text, "html.parser"))[
"createTable"
]
assert create_table_data["canInsertRows"] is True
finally:
ds.close()
@pytest.mark.asyncio
async def test_database_create_table_data_includes_custom_column_types():
ds = Datasette(