Added /-/metadata /-/plugins /-/inspect, closes #225

pull/232/head
Simon Willison 2018-04-18 22:24:48 -07:00
rodzic 6e28ed447f
commit b55809a1e2
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 17E2DEA2588B7F52
5 zmienionych plików z 111 dodań i 14 usunięć

Wyświetl plik

@ -17,7 +17,6 @@ import json
import jinja2 import jinja2
import hashlib import hashlib
import time import time
import pkg_resources
import pint import pint
import pluggy import pluggy
import traceback import traceback
@ -31,6 +30,7 @@ from .utils import (
escape_sqlite, escape_sqlite,
filters_should_redirect, filters_should_redirect,
get_all_foreign_keys, get_all_foreign_keys,
get_plugins,
is_url, is_url,
InvalidSql, InvalidSql,
module_from_path, module_from_path,
@ -402,15 +402,16 @@ class IndexView(RenderMixin):
} }
databases.append(database) databases.append(database)
if as_json: if as_json:
headers = {}
if self.ds.cors:
headers['Access-Control-Allow-Origin'] = '*'
return response.HTTPResponse( return response.HTTPResponse(
json.dumps( json.dumps(
{db['name']: db for db in databases}, {db['name']: db for db in databases},
cls=CustomJSONEncoder cls=CustomJSONEncoder
), ),
content_type='application/json', content_type='application/json',
headers={ headers=headers,
'Access-Control-Allow-Origin': '*'
}
) )
else: else:
return self.render( return self.render(
@ -423,6 +424,32 @@ class IndexView(RenderMixin):
) )
class JsonDataView(RenderMixin):
def __init__(self, datasette, filename, data_callback):
self.ds = datasette
self.jinja_env = datasette.jinja_env
self.filename = filename
self.data_callback = data_callback
async def get(self, request, as_json):
data = self.data_callback()
if as_json:
headers = {}
if self.ds.cors:
headers['Access-Control-Allow-Origin'] = '*'
return response.HTTPResponse(
json.dumps(data),
content_type='application/json',
headers=headers,
)
else:
return self.render(
['show_json.html'],
filename=self.filename,
data=data,
)
async def favicon(request): async def favicon(request):
return response.text('') return response.text('')
@ -1302,15 +1329,25 @@ class Datasette:
for path, dirname in self.static_mounts: for path, dirname in self.static_mounts:
app.static(path, dirname) app.static(path, dirname)
# Mount any plugin static/ directories # Mount any plugin static/ directories
for plugin_module in pm.get_plugins(): for plugin in get_plugins(pm):
try: if plugin['static_path']:
if pkg_resources.resource_isdir(plugin_module.__name__, 'static'): modpath = '/-/static-plugins/{}/'.format(plugin['name'])
modpath = '/-/static-plugins/{}/'.format(plugin_module.__name__) app.static(modpath, plugin['static_path'])
dirpath = pkg_resources.resource_filename(plugin_module.__name__, 'static') app.add_route(
app.static(modpath, dirpath) JsonDataView.as_view(self, 'inspect.json', lambda: self.inspect()),
except (KeyError, ImportError): '/-/inspect<as_json:(\.json)?$>'
# Caused by --plugins_dir= plugins - KeyError/ImportError thrown in Py3.5 )
pass app.add_route(
JsonDataView.as_view(self, 'metadata.json', lambda: self.metadata),
'/-/metadata<as_json:(\.json)?$>'
)
app.add_route(
JsonDataView.as_view(self, 'plugins.json', lambda: [{
'name': p['name'],
'static': p['static_path'] is not None
} for p in get_plugins(pm)]),
'/-/plugins<as_json:(\.json)?$>'
)
app.add_route( app.add_route(
DatabaseView.as_view(self), DatabaseView.as_view(self),
'/<db_name:[^/\.]+?><as_json:(\.jsono?)?$>' '/<db_name:[^/\.]+?><as_json:(\.jsono?)?$>'

Wyświetl plik

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block title %}{{ filename }}{% endblock %}
{% block body_class %}show-json{% endblock %}
{% block content %}
<h1>{{ filename }}</h1>
<pre>{{ data|tojson(indent=4) }}</pre>
{% endblock %}

Wyświetl plik

@ -4,6 +4,7 @@ import hashlib
import imp import imp
import json import json
import os import os
import pkg_resources
import re import re
import shlex import shlex
import sqlite3 import sqlite3
@ -683,3 +684,20 @@ def module_from_path(path, name):
code = compile(file.read(), path, 'exec', dont_inherit=True) code = compile(file.read(), path, 'exec', dont_inherit=True)
exec(code, mod.__dict__) exec(code, mod.__dict__)
return mod return mod
def get_plugins(pm):
plugins = []
for plugin in pm.get_plugins():
static_path = None
try:
if pkg_resources.resource_isdir(plugin.__name__, 'static'):
static_path = pkg_resources.resource_filename(plugin.__name__, 'static')
except (KeyError, ImportError):
# Caused by --plugins_dir= plugins - KeyError/ImportError thrown in Py3.5
pass
plugins.append({
'name': plugin.__name__,
'static_path': static_path,
})
return plugins

Wyświetl plik

@ -29,7 +29,9 @@ def app_client(sql_time_limit_ms=None):
ds.sqlite_functions.append( ds.sqlite_functions.append(
('sleep', 1, lambda n: time.sleep(float(n))), ('sleep', 1, lambda n: time.sleep(float(n))),
) )
yield ds.app().test_client client = ds.app().test_client
client.ds = ds
yield client
def app_client_longer_time_limit(): def app_client_longer_time_limit():

Wyświetl plik

@ -3,6 +3,7 @@ from .fixtures import (
app_client_longer_time_limit, app_client_longer_time_limit,
generate_compound_rows, generate_compound_rows,
generate_sortable_rows, generate_sortable_rows,
METADATA,
) )
import pytest import pytest
@ -629,3 +630,30 @@ def test_plugins_dir_plugin(app_client):
gather_request=False gather_request=False
) )
assert pytest.approx(328.0839) == response.json['rows'][0][0] assert pytest.approx(328.0839) == response.json['rows'][0][0]
def test_metadata_json(app_client):
response = app_client.get(
"/-/metadata.json",
gather_request=False
)
assert METADATA == response.json
def test_inspect_json(app_client):
response = app_client.get(
"/-/inspect.json",
gather_request=False
)
assert app_client.ds.inspect() == response.json
def test_plugins_json(app_client):
response = app_client.get(
"/-/plugins.json",
gather_request=False
)
# This will include any plugins that have been installed into the
# current virtual environment, so we only check for the presence of
# the one we know will definitely be There
assert {'name': 'my_plugin.py', 'static': False} in response.json