BAsic logic for adding view through a plugin
This commit is contained in:
parent
b853f38c74
commit
cc40492f20
|
@ -286,6 +286,7 @@ MIDDLEWARE = tuple(ADDITIONAL_MIDDLEWARES_BEFORE) + (
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"funkwhale_api.users.middleware.RecordActivityMiddleware",
|
"funkwhale_api.users.middleware.RecordActivityMiddleware",
|
||||||
"funkwhale_api.common.middleware.ThrottleStatusMiddleware",
|
"funkwhale_api.common.middleware.ThrottleStatusMiddleware",
|
||||||
|
"funkwhale_api.common.plugins.PluginViewMiddleware",
|
||||||
)
|
)
|
||||||
|
|
||||||
# DEBUG
|
# DEBUG
|
||||||
|
@ -555,6 +556,7 @@ Delay in seconds before uploaded but unattached attachements are pruned from the
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
ROOT_URLCONF = "config.urls"
|
ROOT_URLCONF = "config.urls"
|
||||||
SPA_URLCONF = "config.spa_urls"
|
SPA_URLCONF = "config.spa_urls"
|
||||||
|
PLUGINS_URLCONF = "funkwhale_api.common.plugins"
|
||||||
ASGI_APPLICATION = "config.routing.application"
|
ASGI_APPLICATION = "config.routing.application"
|
||||||
|
|
||||||
# This ensures that Django will be able to detect a secure connection
|
# This ensures that Django will be able to detect a secure connection
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django import http
|
||||||
|
|
||||||
|
from .plugin import PLUGIN
|
||||||
|
|
||||||
|
|
||||||
|
@PLUGIN.register_api_view(path="prometheus")
|
||||||
|
def prometheus(request):
|
||||||
|
stats = get_stats()
|
||||||
|
return http.HttpResponse(json.dumps(stats))
|
||||||
|
|
||||||
|
|
||||||
|
def get_stats():
|
||||||
|
return {"foo": "bar"}
|
|
@ -0,0 +1,5 @@
|
||||||
|
from funkwhale_api.common import plugins
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(plugins.Plugin):
|
||||||
|
name = "prometheus"
|
|
@ -0,0 +1,45 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django import urls
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = []
|
||||||
|
|
||||||
|
|
||||||
|
class PluginViewMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
response = self.get_response(request)
|
||||||
|
if response.status_code == 404 and request.path.startswith("/plugins/"):
|
||||||
|
match = urls.resolve(request.path, urlconf=settings.PLUGINS_URLCONF)
|
||||||
|
response = match.func(request, *match.args, **match.kwargs)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(AppConfig):
|
||||||
|
def ready(self):
|
||||||
|
from . import main # noqa
|
||||||
|
|
||||||
|
return super().ready()
|
||||||
|
|
||||||
|
def register_api_view(self, path, name=None):
|
||||||
|
def register(view):
|
||||||
|
urlpatterns.append(
|
||||||
|
urls.path(
|
||||||
|
"plugins/{}/{}".format(self.name.replace("_", "-"), path),
|
||||||
|
view,
|
||||||
|
name="plugins-{}-{}".format(self.name, name),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
return register
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(name, **kwargs):
|
||||||
|
return urls.reverse(name, settings.PLUGINS_URLCONF, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve(name, **kwargs):
|
||||||
|
return urls.resolve(name, settings.PLUGINS_URLCONF, **kwargs)
|
|
@ -0,0 +1,57 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from django.urls import resolvers
|
||||||
|
|
||||||
|
from funkwhale_api.common import plugins
|
||||||
|
|
||||||
|
|
||||||
|
class P(plugins.Plugin):
|
||||||
|
name = "test_plugin"
|
||||||
|
path = os.path.abspath(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def plugin(settings):
|
||||||
|
yield P(app_name="test_plugin", app_module="tests.common.test_plugins.main.P")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def clear_patterns():
|
||||||
|
plugins.urlpatterns.clear()
|
||||||
|
resolvers._get_cached_resolver.cache_clear()
|
||||||
|
yield
|
||||||
|
resolvers._get_cached_resolver.cache_clear()
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_register_view(plugin, mocker, settings):
|
||||||
|
view = mocker.Mock()
|
||||||
|
plugin.register_api_view("hello", name="hello")(view)
|
||||||
|
expected = "/plugins/test-plugin/hello"
|
||||||
|
assert plugins.reverse("plugins-test_plugin-hello") == expected
|
||||||
|
assert plugins.resolve(expected).func == view
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_view_middleware_not_matching(api_client, plugin, mocker, settings):
|
||||||
|
view = mocker.Mock()
|
||||||
|
get_response = mocker.Mock()
|
||||||
|
middleware = plugins.PluginViewMiddleware(get_response)
|
||||||
|
plugin.register_api_view("hello", name="hello")(view)
|
||||||
|
request = mocker.Mock(path=plugins.reverse("plugins-test_plugin-hello"))
|
||||||
|
response = middleware(request)
|
||||||
|
assert response == get_response.return_value
|
||||||
|
view.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_view_middleware_matching(api_client, plugin, mocker, settings):
|
||||||
|
view = mocker.Mock()
|
||||||
|
get_response = mocker.Mock(return_value=mocker.Mock(status_code=404))
|
||||||
|
middleware = plugins.PluginViewMiddleware(get_response)
|
||||||
|
plugin.register_api_view("hello/<slug:slug>", name="hello")(view)
|
||||||
|
request = mocker.Mock(
|
||||||
|
path=plugins.reverse("plugins-test_plugin-hello", kwargs={"slug": "world"})
|
||||||
|
)
|
||||||
|
response = middleware(request)
|
||||||
|
assert response == view.return_value
|
||||||
|
view.assert_called_once_with(request, slug="world")
|
Loading…
Reference in New Issue