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
	
	 Simon Willison
						Simon Willison