diff --git a/datasette/app.py b/datasette/app.py index 63f99e5f..e7facb19 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -140,6 +140,8 @@ class BaseView(HTTPMethodView): as_json = False extra_template_data = {} start = time.time() + template = self.template + status_code = 200 try: data, extra_template_data = await self.data( request, name, hash, **kwargs @@ -148,7 +150,11 @@ class BaseView(HTTPMethodView): data = { 'ok': False, 'error': str(e), + 'database': name, + 'database_hash': hash, } + template = 'error.html' + status_code = 400 end = time.time() data['query_ms'] = (end - start) * 1000 if as_json: @@ -165,6 +171,7 @@ class BaseView(HTTPMethodView): json.dumps( data, cls=CustomJSONEncoder ), + status=status_code, content_type='application/json', headers={ 'Access-Control-Allow-Origin': '*' @@ -180,10 +187,11 @@ class BaseView(HTTPMethodView): 'url_jsono': path_with_ext(request, '.jsono'), }} r = self.jinja.render( - self.template, + template, request, **context, ) + r.status = status_code # Set far-future cache expiry if self.cache_headers: r.headers['Cache-Control'] = 'max-age={}'.format( diff --git a/datasette/templates/base.html b/datasette/templates/base.html index 3c4ad124..901c92d9 100644 --- a/datasette/templates/base.html +++ b/datasette/templates/base.html @@ -8,9 +8,6 @@ -{% if error %} -
{{ error }}
-{% endif %} {% block content %} {% endblock %} diff --git a/datasette/templates/error.html b/datasette/templates/error.html new file mode 100644 index 00000000..b2bb7140 --- /dev/null +++ b/datasette/templates/error.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title %}{{ database }}{% endblock %} + +{% block content %} +
home / {{ database }}
+ +

{{ database }}

+ +{% if error %} +
{{ error }}
+{% endif %} + +{% endblock %} diff --git a/tests/test_app.py b/tests/test_app.py index 637e6bed..4184e053 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -6,7 +6,7 @@ import tempfile @pytest.fixture(scope='module') -def three_table_app_client(): +def app_client(): with tempfile.TemporaryDirectory() as tmpdir: filepath = os.path.join(tmpdir, 'four_tables.db') conn = sqlite3.connect(filepath) @@ -15,13 +15,13 @@ def three_table_app_client(): yield Datasette([filepath]).app().test_client -def test_homepage(three_table_app_client): - _, response = three_table_app_client.get('/') +def test_homepage(app_client): + _, response = app_client.get('/') assert response.status == 200 assert 'four_tables' in response.text # Now try the JSON - _, response = three_table_app_client.get('/.json') + _, response = app_client.get('/.json') assert response.status == 200 assert response.json.keys() == {'four_tables': 0}.keys() d = response.json['four_tables'] @@ -29,13 +29,13 @@ def test_homepage(three_table_app_client): assert d['tables_count'] == 4 -def test_database_page(three_table_app_client): - _, response = three_table_app_client.get('/four_tables', allow_redirects=False) +def test_database_page(app_client): + _, response = app_client.get('/four_tables', allow_redirects=False) assert response.status == 302 - _, response = three_table_app_client.get('/four_tables') + _, response = app_client.get('/four_tables') assert 'four_tables' in response.text # Test JSON list of tables - _, response = three_table_app_client.get('/four_tables.json') + _, response = app_client.get('/four_tables.json') data = response.json assert 'four_tables' == data['database'] assert [{ @@ -57,8 +57,8 @@ def test_database_page(three_table_app_client): }] == data['tables'] -def test_custom_sql(three_table_app_client): - _, response = three_table_app_client.get( +def test_custom_sql(app_client): + _, response = app_client.get( '/four_tables.jsono?sql=select+content+from+simple_primary_key' ) data = response.json @@ -74,10 +74,24 @@ def test_custom_sql(three_table_app_client): assert 'four_tables' == data['database'] -def test_table_page(three_table_app_client): - _, response = three_table_app_client.get('/four_tables/simple_primary_key') +def test_invalid_custom_sql(app_client): + _, response = app_client.get( + '/four_tables?sql=.schema' + ) + assert response.status == 400 + assert 'Statement must begin with SELECT' in response.text + _, response = app_client.get( + '/four_tables.json?sql=.schema' + ) + assert response.status == 400 + assert response.json['ok'] is False + assert 'Statement must begin with SELECT' == response.json['error'] + + +def test_table_page(app_client): + _, response = app_client.get('/four_tables/simple_primary_key') assert response.status == 200 - _, response = three_table_app_client.get('/four_tables/simple_primary_key.jsono') + _, response = app_client.get('/four_tables/simple_primary_key.jsono') assert response.status == 200 data = response.json assert data['query']['sql'] == 'select * from "simple_primary_key" order by pk limit 51' @@ -91,10 +105,10 @@ def test_table_page(three_table_app_client): }] -def test_view(three_table_app_client): - _, response = three_table_app_client.get('/four_tables/simple_view') +def test_view(app_client): + _, response = app_client.get('/four_tables/simple_view') assert response.status == 200 - _, response = three_table_app_client.get('/four_tables/simple_view.jsono') + _, response = app_client.get('/four_tables/simple_view.jsono') assert response.status == 200 data = response.json assert data['rows'] == [{