kopia lustrzana https://github.com/simonw/datasette
Edit SQL button on canned queries, closes #1019
rodzic
acf07a6772
commit
f3a087a578
|
|
@ -447,3 +447,9 @@ svg.dropdown-menu-icon {
|
|||
border-right: 5px solid transparent;
|
||||
border-bottom: 5px solid #666;
|
||||
}
|
||||
|
||||
.canned-query-edit-sql {
|
||||
padding-left: 0.5em;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
<button id="sql-format" type="button" hidden>Format SQL</button>
|
||||
{% if canned_write %}<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">{% endif %}
|
||||
<input type="submit" value="Run SQL">
|
||||
{% if canned_query and edit_sql_url %}<a href="{{ edit_sql_url }}" class="canned-query-edit-sql">Edit SQL</a>{% endif %}
|
||||
</p>
|
||||
</form>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import itertools
|
||||
import jinja2
|
||||
import json
|
||||
from urllib.parse import parse_qsl
|
||||
from urllib.parse import parse_qsl, urlencode
|
||||
|
||||
from datasette.utils import (
|
||||
check_visibility,
|
||||
|
|
@ -11,6 +11,7 @@ from datasette.utils import (
|
|||
is_url,
|
||||
path_with_added_args,
|
||||
path_with_removed_args,
|
||||
InvalidSql,
|
||||
)
|
||||
from datasette.utils.asgi import AsgiFileDownload, Response, Forbidden
|
||||
from datasette.plugins import pm
|
||||
|
|
@ -301,6 +302,10 @@ class QueryView(DataView):
|
|||
),
|
||||
)
|
||||
|
||||
allow_execute_sql = await self.ds.permission_allowed(
|
||||
request.actor, "execute-sql", database, default=True
|
||||
)
|
||||
|
||||
async def extra_template():
|
||||
display_rows = []
|
||||
for row in results.rows:
|
||||
|
|
@ -329,12 +334,38 @@ class QueryView(DataView):
|
|||
)
|
||||
display_row.append(display_value)
|
||||
display_rows.append(display_row)
|
||||
|
||||
# Show 'Edit SQL' button only if:
|
||||
# - User is allowed to execute SQL
|
||||
# - SQL is an approved SELECT statement
|
||||
# - No magic parameters, so no :_ in the SQL string
|
||||
edit_sql_url = None
|
||||
is_validated_sql = False
|
||||
try:
|
||||
validate_sql_select(sql)
|
||||
is_validated_sql = True
|
||||
except InvalidSql:
|
||||
pass
|
||||
if allow_execute_sql and is_validated_sql and ":_" not in sql:
|
||||
edit_sql_url = (
|
||||
self.database_url(database)
|
||||
+ "?"
|
||||
+ urlencode(
|
||||
{
|
||||
**{
|
||||
"sql": sql,
|
||||
},
|
||||
**named_parameter_values,
|
||||
}
|
||||
)
|
||||
)
|
||||
return {
|
||||
"display_rows": display_rows,
|
||||
"custom_sql": True,
|
||||
"named_parameter_values": named_parameter_values,
|
||||
"editable": editable,
|
||||
"canned_query": canned_query,
|
||||
"edit_sql_url": edit_sql_url,
|
||||
"metadata": metadata,
|
||||
"config": self.ds.config_dict(),
|
||||
"request": request,
|
||||
|
|
@ -352,9 +383,7 @@ class QueryView(DataView):
|
|||
"columns": columns,
|
||||
"query": {"sql": sql, "params": params},
|
||||
"private": private,
|
||||
"allow_execute_sql": await self.ds.permission_allowed(
|
||||
request.actor, "execute-sql", database, default=True
|
||||
),
|
||||
"allow_execute_sql": allow_execute_sql,
|
||||
},
|
||||
extra_template,
|
||||
templates,
|
||||
|
|
|
|||
|
|
@ -1403,3 +1403,48 @@ def test_base_url_config(base_url, path):
|
|||
"href_or_src": href,
|
||||
"element_parent": str(el.parent),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,expected",
|
||||
[
|
||||
(
|
||||
"/fixtures/neighborhood_search",
|
||||
"/fixtures?sql=%0Aselect+neighborhood%2C+facet_cities.name%2C+state%0Afrom+facetable%0A++++join+facet_cities%0A++++++++on+facetable.city_id+%3D+facet_cities.id%0Awhere+neighborhood+like+%27%25%27+%7C%7C+%3Atext+%7C%7C+%27%25%27%0Aorder+by+neighborhood%3B%0A&text=",
|
||||
),
|
||||
(
|
||||
"/fixtures/neighborhood_search?text=ber",
|
||||
"/fixtures?sql=%0Aselect+neighborhood%2C+facet_cities.name%2C+state%0Afrom+facetable%0A++++join+facet_cities%0A++++++++on+facetable.city_id+%3D+facet_cities.id%0Awhere+neighborhood+like+%27%25%27+%7C%7C+%3Atext+%7C%7C+%27%25%27%0Aorder+by+neighborhood%3B%0A&text=ber",
|
||||
),
|
||||
("/fixtures/pragma_cache_size", None),
|
||||
(
|
||||
"/fixtures/𝐜𝐢𝐭𝐢𝐞𝐬",
|
||||
"/fixtures?sql=select+id%2C+name+from+facet_cities+order+by+id+limit+1%3B",
|
||||
),
|
||||
("/fixtures/magic_parameters", None),
|
||||
],
|
||||
)
|
||||
def test_edit_sql_link_on_canned_queries(app_client, path, expected):
|
||||
response = app_client.get(path)
|
||||
expected_link = '<a href="{}" class="canned-query-edit-sql">Edit SQL</a>'.format(
|
||||
expected
|
||||
)
|
||||
if expected:
|
||||
assert expected_link in response.text
|
||||
else:
|
||||
assert "Edit SQL" not in response.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize("permission_allowed", [True, False])
|
||||
def test_edit_sql_link_not_shown_if_user_lacks_permission(permission_allowed):
|
||||
with make_app_client(
|
||||
metadata={
|
||||
"allow_sql": None if permission_allowed else {"id": "not-you"},
|
||||
"databases": {"fixtures": {"queries": {"simple": "select 1 + 1"}}},
|
||||
}
|
||||
) as client:
|
||||
response = client.get("/fixtures/simple")
|
||||
if permission_allowed:
|
||||
assert "Edit SQL" in response.text
|
||||
else:
|
||||
assert "Edit SQL" not in response.text
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue