Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
tarator
Funkwhale
Commits
8c587d07
Unverified
Commit
8c587d07
authored
4 years ago
by
Agate
Browse files
Options
Download
Email Patches
Plain Diff
Moar plugins polishing and sugar
parent
4c4ab591
plugins-v3
No related merge requests found
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
api/config/plugins.py
+31
-2
api/config/plugins.py
api/config/settings/common.py
+6
-4
api/config/settings/common.py
api/funkwhale_api/cli/plugins.py
+65
-8
api/funkwhale_api/cli/plugins.py
api/funkwhale_api/common/migrations/0008_auto_20200617_1902.py
+29
-0
...unkwhale_api/common/migrations/0008_auto_20200617_1902.py
api/funkwhale_api/common/models.py
+15
-0
api/funkwhale_api/common/models.py
api/funkwhale_api/common/serializers.py
+18
-0
api/funkwhale_api/common/serializers.py
api/funkwhale_api/plugins/funkwhale_plugin_prometheus/README.md
+0
-0
...nkwhale_api/plugins/funkwhale_plugin_prometheus/README.md
api/funkwhale_api/plugins/funkwhale_plugin_prometheus/__init__.py
+0
-0
...whale_api/plugins/funkwhale_plugin_prometheus/__init__.py
api/funkwhale_api/plugins/funkwhale_plugin_prometheus/entrypoint.py
+3
-2
...ale_api/plugins/funkwhale_plugin_prometheus/entrypoint.py
api/funkwhale_api/plugins/funkwhale_plugin_prometheus/setup.cfg
+2
-2
...nkwhale_api/plugins/funkwhale_plugin_prometheus/setup.cfg
api/funkwhale_api/plugins/funkwhale_plugin_prometheus/setup.py
+0
-0
...unkwhale_api/plugins/funkwhale_plugin_prometheus/setup.py
api/tests/common/test_plugins.py
+34
-0
api/tests/common/test_plugins.py
api/tests/common/test_serializers.py
+19
-0
api/tests/common/test_serializers.py
api/tests/conftest.py
+11
-0
api/tests/conftest.py
docs/developers/index.rst
+1
-0
docs/developers/index.rst
docs/developers/plugins.rst
+8
-0
docs/developers/plugins.rst
with
242 additions
and
18 deletions
+242
-18
api/config/plugins.py
+
31
-
2
View file @
8c587d07
...
...
@@ -14,9 +14,25 @@ class ConfigError(ValueError):
class
Plugin
(
AppConfig
):
conf
=
{}
path
=
"noop"
conf_serializer
=
None
def
get_conf
(
self
):
return
{
"enabled"
:
self
.
plugin_settings
.
enabled
}
return
self
.
instance
.
conf
def
set_conf
(
self
,
data
):
if
self
.
conf_serializer
:
s
=
self
.
conf_serializer
(
data
=
data
)
s
.
is_valid
(
raise_exception
=
True
)
data
=
s
.
validated_data
instance
=
self
.
instance
()
instance
.
conf
=
data
instance
.
save
(
update_fields
=
[
"conf"
])
def
instance
(
self
):
"""Return the DB object that match the plugin"""
from
funkwhale_api.common
import
models
return
models
.
PodPlugin
.
objects
.
get_or_create
(
code
=
self
.
name
)[
0
]
def
plugin_settings
(
self
):
"""
...
...
@@ -94,7 +110,13 @@ plugins_manager.add_hookspecs(HookSpec())
def
register
(
plugin_class
):
return
plugins_manager
.
register
(
plugin_class
(
"noop"
,
"noop"
))
return
plugins_manager
.
register
(
plugin_class
(
plugin_class
.
name
,
"noop"
))
def
save
(
plugin_class
):
from
funkwhale_api.common.models
import
PodPlugin
return
PodPlugin
.
objects
.
get_or_create
(
code
=
plugin_class
.
name
)[
0
]
def
trigger_hook
(
name
,
*
args
,
**
kwargs
):
...
...
@@ -104,6 +126,13 @@ def trigger_hook(name, *args, **kwargs):
@
register
class
DefaultPlugin
(
Plugin
):
name
=
"default"
verbose_name
=
"Default plugin"
@
plugin_hook
def
database_engine
(
self
):
return
"django.db.backends.postgresql"
@
plugin_hook
def
urls
(
self
):
return
[]
This diff is collapsed.
Click to expand it.
api/config/settings/common.py
+
6
-
4
View file @
8c587d07
...
...
@@ -24,7 +24,11 @@ class Plugins(persisting_theory.Registry):
look_into
=
"entrypoint"
PLUGINS
=
[
p
for
p
in
env
.
list
(
"FUNKWHALE_PLUGINS"
,
default
=
[])
if
p
]
PLUGINS
=
[
"funkwhale_plugin_{}"
.
format
(
p
)
for
p
in
env
.
list
(
"FUNKWHALE_PLUGINS"
,
default
=
[])
if
p
]
"""
List of Funkwhale plugins to load.
"""
...
...
@@ -33,8 +37,6 @@ from config import plugins # noqa
plugins_registry
=
Plugins
()
plugins_registry
.
autodiscover
(
PLUGINS
)
# plugins.plugins_manager.register(Plugin("noop", "noop"))
LOGLEVEL
=
env
(
"LOGLEVEL"
,
default
=
"info"
).
upper
()
"""
Default logging level for the Funkwhale processes"""
# pylint: disable=W0105
...
...
@@ -272,7 +274,7 @@ List of Django apps to load in addition to Funkwhale plugins and apps.
PLUGINS_APPS
=
tuple
()
for
p
in
plugins
.
trigger_hook
(
"register_apps"
):
PLUGINS_APPS
+=
(
p
,
)
PLUGINS_APPS
+=
tuple
(
p
)
INSTALLED_APPS
=
(
DJANGO_APPS
...
...
This diff is collapsed.
Click to expand it.
api/funkwhale_api/cli/plugins.py
+
65
-
8
View file @
8c587d07
import
os
import
shutil
import
subprocess
import
sys
import
tempfile
import
click
...
...
@@ -8,12 +11,27 @@ from django.conf import settings
from
.
import
base
PIP
=
os
.
path
.
join
(
sys
.
prefix
,
"bin"
,
"pip"
)
@
base
.
cli
.
group
()
def
plugins
():
"""Install, configure and remove plugins"""
pass
def
get_all_plugins
():
plugins
=
[
f
.
path
for
f
in
os
.
scandir
(
settings
.
FUNKWHALE_PLUGINS_PATH
)
if
"/funkwhale_plugin_"
in
f
.
path
]
plugins
=
[
p
.
split
(
"-"
)[
0
].
split
(
"/"
)[
-
1
].
replace
(
"funkwhale_plugin_"
,
""
)
for
p
in
plugins
]
return
plugins
@
plugins
.
command
(
"install"
)
@
click
.
argument
(
"name_or_url"
,
nargs
=-
1
)
@
click
.
option
(
"--builtins"
,
is_flag
=
True
)
...
...
@@ -21,17 +39,56 @@ def plugins():
def
install
(
name_or_url
,
builtins
,
pip_args
):
"""
Installed the specified plug using their name.
If --builtins is provided, it will also install
plugins present at FUNKWHALE_PLUGINS_PATH
"""
pip_args
=
pip_args
or
""
target_path
=
settings
.
FUNKWHALE_PLUGINS_PATH
builtins_path
=
os
.
path
.
join
(
settings
.
APPS_DIR
,
"plugins"
)
builtins_plugins
=
[
f
.
path
for
f
in
os
.
scandir
(
builtins_path
)
if
f
.
is_dir
()]
command
=
"pip install {} --target={} {}"
.
format
(
pip_args
,
target_path
,
" "
.
join
(
builtins_plugins
)
all_plugins
=
[]
for
p
in
name_or_url
:
builtin_path
=
os
.
path
.
join
(
settings
.
APPS_DIR
,
"plugins"
,
"funkwhale_plugin_{}"
.
format
(
p
)
)
if
os
.
path
.
exists
(
builtin_path
):
all_plugins
.
append
(
builtin_path
)
else
:
all_plugins
.
append
(
p
)
install_plugins
(
pip_args
,
all_plugins
)
click
.
echo
(
"Installation completed, ensure FUNKWHALE_PLUGINS={} is present in your .env file"
.
format
(
","
.
join
(
get_all_plugins
())
)
)
def
install_plugins
(
pip_args
,
all_plugins
):
with
tempfile
.
TemporaryDirectory
()
as
tmpdirname
:
command
=
"{} install {} --target {} --build={} {}"
.
format
(
PIP
,
pip_args
,
settings
.
FUNKWHALE_PLUGINS_PATH
,
tmpdirname
,
" "
.
join
(
all_plugins
),
)
subprocess
.
run
(
command
,
shell
=
True
,
check
=
True
,
)
@
plugins
.
command
(
"uninstall"
)
@
click
.
argument
(
"name"
,
nargs
=-
1
)
def
uninstall
(
name
):
"""
Remove plugins
"""
to_remove
=
[
"funkwhale_plugin_{}"
.
format
(
n
)
for
n
in
name
]
command
=
"{} uninstall -y {}"
.
format
(
PIP
,
" "
.
join
(
to_remove
))
subprocess
.
run
(
command
,
shell
=
True
,
check
=
True
,
)
for
f
in
os
.
scandir
(
settings
.
FUNKWHALE_PLUGINS_PATH
):
for
n
in
name
:
if
"/funkwhale_plugin_{}"
.
format
(
n
)
in
f
.
path
:
shutil
.
rmtree
(
f
.
path
)
click
.
echo
(
"Removal completed, set FUNKWHALE_PLUGINS={} in your .env file"
.
format
(
","
.
join
(
get_all_plugins
())
)
)
This diff is collapsed.
Click to expand it.
api/funkwhale_api/common/migrations/0008_auto_20200617_1902.py
0 → 100644
+
29
-
0
View file @
8c587d07
# Generated by Django 3.0.6 on 2020-06-17 19:02
import
django.contrib.postgres.fields.jsonb
from
django.db
import
migrations
,
models
import
django.utils.timezone
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'common'
,
'0007_auto_20200116_1610'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'PodPlugin'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
verbose_name
=
'ID'
)),
(
'conf'
,
django
.
contrib
.
postgres
.
fields
.
jsonb
.
JSONField
(
default
=
None
,
null
=
True
,
blank
=
True
)),
(
'code'
,
models
.
CharField
(
max_length
=
100
,
unique
=
True
)),
(
'creation_date'
,
models
.
DateTimeField
(
default
=
django
.
utils
.
timezone
.
now
)),
],
),
migrations
.
AlterField
(
model_name
=
'attachment'
,
name
=
'url'
,
field
=
models
.
URLField
(
blank
=
True
,
max_length
=
500
,
null
=
True
),
),
]
This diff is collapsed.
Click to expand it.
api/funkwhale_api/common/models.py
+
15
-
0
View file @
8c587d07
...
...
@@ -18,6 +18,7 @@ from django.urls import reverse
from
versatileimagefield.fields
import
VersatileImageField
from
versatileimagefield.image_warmer
import
VersatileImageFieldWarmer
from
config
import
plugins
from
funkwhale_api.federation
import
utils
as
federation_utils
from
.
import
utils
...
...
@@ -363,3 +364,17 @@ def remove_attached_content(sender, instance, **kwargs):
getattr
(
instance
,
field
).
delete
()
except
Content
.
DoesNotExist
:
pass
class
PodPlugin
(
models
.
Model
):
conf
=
JSONField
(
default
=
None
,
null
=
True
,
blank
=
True
)
code
=
models
.
CharField
(
max_length
=
100
,
unique
=
True
)
creation_date
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
@
property
def
plugin
(
self
):
"""Links to the Plugin instance in entryposint.py"""
candidates
=
plugins
.
plugins_manager
.
get_plugins
()
for
p
in
candidates
:
if
p
.
name
==
self
.
code
:
return
p
This diff is collapsed.
Click to expand it.
api/funkwhale_api/common/serializers.py
+
18
-
0
View file @
8c587d07
...
...
@@ -339,3 +339,21 @@ class NullToEmptDict(object):
if
not
v
:
return
v
return
super
().
to_representation
(
v
)
class
PodPluginSerializer
(
serializers
.
Serializer
):
code
=
serializers
.
CharField
(
read_only
=
True
)
enabled
=
serializers
.
BooleanField
()
conf
=
serializers
.
JSONField
()
label
=
serializers
.
SerializerMethodField
()
class
Meta
:
fields
=
[
"code"
,
"label"
,
"enabled"
,
"conf"
,
]
def
get_label
(
self
,
o
):
return
o
.
plugin
.
verbose_name
This diff is collapsed.
Click to expand it.
api/funkwhale_api/plugins/prometheus
_exporter
/README.md
→
api/funkwhale_api/plugins/
funkwhale_plugin_
prometheus/README.md
+
0
-
0
View file @
8c587d07
File moved
This diff is collapsed.
Click to expand it.
api/funkwhale_api/plugins/prometheus
_exporter
/__init__.py
→
api/funkwhale_api/plugins/
funkwhale_plugin_
prometheus/__init__.py
+
0
-
0
View file @
8c587d07
File moved
This diff is collapsed.
Click to expand it.
api/funkwhale_api/plugins/prometheus
_exporter
/entrypoint.py
→
api/funkwhale_api/plugins/
funkwhale_plugin_
prometheus/entrypoint.py
+
3
-
2
View file @
8c587d07
...
...
@@ -5,7 +5,8 @@ from config import plugins
@
plugins
.
register
class
Plugin
(
plugins
.
Plugin
):
name
=
"prometheus_exporter"
name
=
"funkwhale_plugin_prometheus"
verbose_name
=
"Prometheus metrics exporter"
@
plugins
.
plugin_hook
def
database_engine
(
self
):
...
...
@@ -13,7 +14,7 @@ class Plugin(plugins.Plugin):
@
plugins
.
plugin_hook
def
register_apps
(
self
):
return
"django_prometheus"
return
[
"django_prometheus"
]
@
plugins
.
plugin_hook
def
middlewares_before
(
self
):
...
...
This diff is collapsed.
Click to expand it.
api/funkwhale_api/plugins/prometheus
_exporter
/setup.cfg
→
api/funkwhale_api/plugins/
funkwhale_plugin_
prometheus/setup.cfg
+
2
-
2
View file @
8c587d07
[metadata]
name = funkwhale-prometheus
description = "A prometheus metric exporter for your Funkwhale pod"
name = funkwhale-
plugin-
prometheus
description = "A prometheus metric
s
exporter for your Funkwhale pod"
version = 0.1.dev0
author = Agate Blue
author_email = me@agate.blue
...
...
This diff is collapsed.
Click to expand it.
api/funkwhale_api/plugins/prometheus
_exporter
/setup.py
→
api/funkwhale_api/plugins/
funkwhale_plugin_
prometheus/setup.py
+
0
-
0
View file @
8c587d07
File moved
This diff is collapsed.
Click to expand it.
api/tests/common/test_plugins.py
+
34
-
0
View file @
8c587d07
import
pytest
from
rest_framework
import
serializers
from
config
import
plugins
from
funkwhale_api.common
import
models
def
test_plugin_validate_set_conf
():
class
S
(
serializers
.
Serializer
):
test
=
serializers
.
CharField
()
foo
=
serializers
.
BooleanField
()
class
P
(
plugins
.
Plugin
):
conf_serializer
=
S
p
=
P
(
"noop"
,
"noop"
)
with
pytest
.
raises
(
serializers
.
ValidationError
):
assert
p
.
set_conf
({
"test"
:
"hello"
,
"foo"
:
"bar"
})
def
test_plugin_validate_set_conf_persists
():
class
S
(
serializers
.
Serializer
):
test
=
serializers
.
CharField
()
foo
=
serializers
.
BooleanField
()
class
P
(
plugins
.
Plugin
):
name
=
"test_plugin"
conf_serializer
=
S
p
=
P
(
"noop"
,
"noop"
)
p
.
set_conf
({
"test"
:
"hello"
,
"foo"
:
False
})
assert
p
.
instance
()
==
models
.
PodPlugin
.
objects
.
latest
(
"id"
)
assert
p
.
instance
().
conf
==
{
"test"
:
"hello"
,
"foo"
:
False
}
This diff is collapsed.
Click to expand it.
api/tests/common/test_serializers.py
+
19
-
0
View file @
8c587d07
...
...
@@ -6,6 +6,7 @@ from django.urls import reverse
import
django_filters
from
config
import
plugins
from
funkwhale_api.common
import
serializers
from
funkwhale_api.common
import
utils
from
funkwhale_api.users
import
models
...
...
@@ -267,3 +268,21 @@ def test_content_serializer(factories):
serializer
=
serializers
.
ContentSerializer
(
content
)
assert
serializer
.
data
==
expected
def
test_plugin_serializer
():
class
TestPlugin
(
plugins
.
Plugin
):
name
=
"test_plugin"
verbose_name
=
"A test plugin"
plugins
.
register
(
TestPlugin
)
instance
=
plugins
.
save
(
TestPlugin
)
assert
isinstance
(
instance
.
plugin
,
TestPlugin
)
expected
=
{
"code"
:
"test_plugin"
,
"label"
:
"A test plugin"
,
"enabled"
:
True
,
"conf"
:
None
,
}
assert
serializers
.
PodPluginSerializer
(
instance
).
data
==
expected
This diff is collapsed.
Click to expand it.
api/tests/conftest.py
+
11
-
0
View file @
8c587d07
...
...
@@ -24,6 +24,7 @@ from aioresponses import aioresponses
from
dynamic_preferences.registries
import
global_preferences_registry
from
rest_framework.test
import
APIClient
,
APIRequestFactory
from
config
import
plugins
from
funkwhale_api.activity
import
record
from
funkwhale_api.federation
import
actors
from
funkwhale_api.moderation
import
mrf
...
...
@@ -429,3 +430,13 @@ def clear_license_cache(db):
@
pytest
.
fixture
def
faker
():
return
factory
.
Faker
.
_get_faker
()
@
pytest
.
fixture
def
plugins_manager
():
return
plugins
.
PluginManager
(
"tests"
)
@
pytest
.
fixture
def
hook
(
plugins_manager
):
return
plugins
.
HookimplMarker
(
"tests"
)
This diff is collapsed.
Click to expand it.
docs/developers/index.rst
+
1
-
0
View file @
8c587d07
...
...
@@ -13,5 +13,6 @@ Reference
architecture
../api
./authentication
./plugins
../federation/index
subsonic
This diff is collapsed.
Click to expand it.
docs/developers/plugins.rst
0 → 100644
+
8
-
0
View file @
8c587d07
Funkwhale Plugins
=================
With version 1.0, Funkwhale makes it possible for third party to write plugins
and distribute them.
Funkwhale plugins are regular django apps, that can register models, API
endpoints, and react to specific events (e.g a son was listened, a federation message was delivered, etc.)
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help