BAsic logic for adding view through a plugin

This commit is contained in:
Agate 2020-06-14 13:46:04 +02:00
parent b853f38c74
commit cc40492f20
No known key found for this signature in database
GPG Key ID: 6B501DFD73514E14
6 changed files with 124 additions and 0 deletions

View File

@ -286,6 +286,7 @@ MIDDLEWARE = tuple(ADDITIONAL_MIDDLEWARES_BEFORE) + (
"django.contrib.messages.middleware.MessageMiddleware",
"funkwhale_api.users.middleware.RecordActivityMiddleware",
"funkwhale_api.common.middleware.ThrottleStatusMiddleware",
"funkwhale_api.common.plugins.PluginViewMiddleware",
)
# DEBUG
@ -555,6 +556,7 @@ Delay in seconds before uploaded but unattached attachements are pruned from the
# ------------------------------------------------------------------------------
ROOT_URLCONF = "config.urls"
SPA_URLCONF = "config.spa_urls"
PLUGINS_URLCONF = "funkwhale_api.common.plugins"
ASGI_APPLICATION = "config.routing.application"
# This ensures that Django will be able to detect a secure connection

View File

View File

@ -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"}

View File

@ -0,0 +1,5 @@
from funkwhale_api.common import plugins
class Plugin(plugins.Plugin):
name = "prometheus"

View File

@ -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)

View File

@ -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")