Skip to content

Commit

Permalink
Edit SQL button on canned queries, closes #1019
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw committed Oct 14, 2020
1 parent acf07a6 commit f3a087a
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 4 deletions.
6 changes: 6 additions & 0 deletions datasette/static/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions datasette/templates/query.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ <h3>Query parameters</h3>
<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>

Expand Down
37 changes: 33 additions & 4 deletions datasette/views/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
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,
Expand All @@ -11,6 +11,7 @@
is_url,
path_with_added_args,
path_with_removed_args,
InvalidSql,
)
from datasette.utils.asgi import AsgiFileDownload, Response, Forbidden
from datasette.plugins import pm
Expand Down Expand Up @@ -301,6 +302,10 @@ async def extra_template():
),
)

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:
Expand Down Expand Up @@ -329,12 +334,38 @@ async def extra_template():
)
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,
Expand All @@ -352,9 +383,7 @@ async def extra_template():
"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,
Expand Down
45 changes: 45 additions & 0 deletions tests/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -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&amp;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&amp;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

0 comments on commit f3a087a

Please sign in to comment.