-
Notifications
You must be signed in to change notification settings - Fork 54
Draft: add rdmo.config app for Plugin model (plugin managament)
#1436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MyPyDavid
wants to merge
112
commits into
2.5.0/release
Choose a base branch
from
add-config-app-for-plugin-model
base: 2.5.0/release
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
112 commits
Select commit
Hold shift + click to select a range
f90b6e3
initial add config app with Plugin model
MyPyDavid fc11cd2
core: add rdmo.config to settings
MyPyDavid 061ce24
config: add plugin_settings JSONField
MyPyDavid 7e07fd1
config(admin): update fields and re-use ElementAdminForm
MyPyDavid f764449
config(tests): initial add tests for plugins
MyPyDavid bf829e7
config(migrations): add 0001_initial again
MyPyDavid 0eba427
config(viewsets): add v1-config to api and plugins viewset
MyPyDavid 921c9ec
config(plugin): add managers and plugin_type property
MyPyDavid 2856b20
config(commands): add check_plugins
MyPyDavid 8d29e6f
config(tests): add model, admin, validator, viewset tests
MyPyDavid fdb4ca7
questions(migrations): fix 0032_meta by adding dependency to domain.0038
MyPyDavid 2bf6779
testing: add mock plugins and config fixtures
MyPyDavid 4ebc212
config(tests): update and fix tests
MyPyDavid 38cd0a9
config(plugin): add initialize_class method
MyPyDavid 864e0fa
rules: add config.plugin model and object perms
MyPyDavid 52a9975
style: add eof newline
MyPyDavid 7309a45
style: add newline
MyPyDavid 5d464bf
config(apps): call it Config
MyPyDavid 8e740d7
core(settings): add PLUGINS setting with default list
MyPyDavid 1ba1ad1
config(commands): add setup_plugins
MyPyDavid 147ea53
testing: add config fixture and mock plugins
MyPyDavid 1617950
config(checks): add plugin settings check
MyPyDavid 0702045
config(plugins): refactor from core and update utils
MyPyDavid dd365ba
core(settings): remove deprecated plugin settings
MyPyDavid ba1bfea
core(plugins): revert refactor to config
MyPyDavid e75704f
Move mock SimpleProviders to testing
MyPyDavid 90f6b7a
options(viewsets): handle missing provider setting
MyPyDavid 41feaa0
management(rules): fix plugin toggle site pred
MyPyDavid 07ee628
config(plugins): add support for xml import
MyPyDavid f67c629
core(managers): add filter-for-... mixins and refactor
MyPyDavid 50140f6
core(constants): add config.plugin to PERMISSIONS
MyPyDavid fbf7a38
config(plugin): add PluginManager to model
MyPyDavid 6859dfd
Add python_path and plugin_type to PluginSerializer
MyPyDavid 8935833
Fix mocked plugin in test
MyPyDavid c44ad89
testing: add rdmo internal classes to plugin setting and fixtures
MyPyDavid 9c9f73d
Add url_name field to Plugin model
MyPyDavid 08b9527
Add default to url_name field
MyPyDavid 7f05adb
Update and refactor checks and commands
MyPyDavid 57c5e90
testing: update plugin settings and fixtures
MyPyDavid b47bff9
Add url_name and plugin_settings to PluginSerializer
MyPyDavid d4e2d1b
Update and refactor config utils and methods
MyPyDavid 64c18f5
Use new plugins in projects
MyPyDavid 287feed
config: refactor plugin related parts
MyPyDavid 2b3e81b
config(commands): fix get or initialize in setup_plugins
MyPyDavid ccaab58
config(plugin): add for_context to PluginManager
MyPyDavid 460c62c
Add plugin_type field to Plugin
MyPyDavid bbe0fcf
config(tests): update tests
MyPyDavid 545a4cf
Add plugins m2m field to OptionSet
MyPyDavid 88a6b0d
Remove args from core.plugins.Plugin and add plugin_type to inheritin…
MyPyDavid c6693c5
Update initialize_class method on Plugin
MyPyDavid fa930db
Add support for new Plugin objects in projects
MyPyDavid 7990ba5
testing: remove legacy plugin settings from base.py settings
MyPyDavid a38a302
config(checks): update output and add tests
MyPyDavid 9f9dff4
config(tests): add fixture to enable legacy settings
MyPyDavid 3469914
update Plugin model, use string import for Catalog
MyPyDavid ce9c820
add url_name to Plugin import
MyPyDavid 627df20
update detect plugin type
MyPyDavid 29daaf5
move legacy Plugin base class from core to config
MyPyDavid b585b86
use plugins instead of OptionSet.provider
MyPyDavid 14de0fd
testing(config): fix fixtures and add plugin_type
MyPyDavid aaf9609
testing(config): add URLImport plugin
MyPyDavid 317e07b
testing(settings): add URLImport plugin
MyPyDavid 394114d
testing(fixtures): add simple plugin to optionset
MyPyDavid d0b01e2
Add filters for format and settings to Plugin manager
MyPyDavid 5e47f1e
Add PluginPythonPathValidator
MyPyDavid 2d4a56a
style: rename unused var
MyPyDavid 0491e35
Update project imports test
MyPyDavid f07c4d6
Use Plugin queryset for project imports action
MyPyDavid 5f5fe9c
Use Plugin filter query for export view
MyPyDavid a6cead2
Use Plugin filter query for snapshot export
MyPyDavid cecf9a3
Use Plugin filter for integration issue provider
MyPyDavid 8e79f79
testing(fixtures): add simple issue provider to config
MyPyDavid 4bbad43
Use only ForSiteQuerySet mixin in PluginQuerySet
MyPyDavid 6ec2d43
Clean up in config app
MyPyDavid d2ca0dc
Update Plugin manager
MyPyDavid 707f566
Use for_context filter for Plugin in projects
MyPyDavid 6754001
tests: add catalog 1 to plugin fixtures
MyPyDavid a523400
Update get export plugin in project and snaphsot export view
MyPyDavid 0d7c42b
tests: add catalog 1 to all plugin fixtures
MyPyDavid 2a77e49
tests: fix assert in prune projects test
MyPyDavid 8aabf3f
Use Plugin filter in integration form
MyPyDavid 32d408c
Use Plugin filter in export projects command
MyPyDavid 42a54e6
Use Plugin filter in oauth callbck view
MyPyDavid d3f2663
Clean up legacy get_plugin
MyPyDavid 07f8414
Add Plugin to management UI
MyPyDavid 106d178
Fix typo in config managers
MyPyDavid b6260c9
Fix mocked plugin in test
MyPyDavid a68adca
Fix timestamps in config fixtures
MyPyDavid 03faa2d
Add get_plugin_python_paths util func
MyPyDavid bc1d19f
Use get_plugin_python_paths as validation in admin and api
MyPyDavid 1f2ecfd
Use Select for python_path field in EditPlugin
MyPyDavid 6f3a3bd
Use enum constant for internal plugin types
MyPyDavid 0ffb48a
Clean up use of plugin types
MyPyDavid 35b3bdd
Update setup plugins script
MyPyDavid 8f66bac
Rename function
MyPyDavid 9108f28
Use variables directly from options kwargs
MyPyDavid 0ffdc3b
Clean up comment
MyPyDavid d4e590a
Clean up newline
MyPyDavid ed97b32
Add newline
MyPyDavid 3445ee2
Clean up and refactor config app
MyPyDavid b176e23
Clean up and refactor plugins in projects
MyPyDavid 92ee088
Remove queryset that uses OPTIONSET_PROVIDERS
MyPyDavid 7b7b2f5
Clean up and refactor plugins in services
MyPyDavid 06a503e
Add queryset from plugins to ProviderViewSet and add test
MyPyDavid 5c1ae6e
Clean up in config.legacy
MyPyDavid 9ecf292
Add support for default uri prefix to plugins
MyPyDavid ce6e187
Use meta model name instead
MyPyDavid 3f7b5aa
Remove CSV and JSON exports from PLUGINS
MyPyDavid 4d46f64
Clean up config.helpers and imported annotations
MyPyDavid 366c7ad
Do not raise from Exception in config
MyPyDavid 1b178d1
Make str return just uri for Plugin
MyPyDavid af00d09
Improve error handling for setup plugins
MyPyDavid File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| from django import forms | ||
| from django.contrib import admin | ||
|
|
||
| from rdmo.core.admin import ElementAdminForm | ||
| from rdmo.core.utils import get_language_fields, get_plugin_python_paths | ||
|
|
||
| from .models import Plugin | ||
| from .validators import PluginLockedValidator, PluginPythonPathValidator, PluginUniqueURIValidator | ||
|
|
||
|
|
||
| class PluginAdminForm(ElementAdminForm): | ||
|
|
||
| python_path = forms.ChoiceField(choices=[(plugin, plugin) for plugin in get_plugin_python_paths()]) | ||
|
|
||
|
|
||
| class Meta: | ||
| model = Plugin | ||
| fields = '__all__' | ||
|
|
||
| def clean(self): | ||
| PluginUniqueURIValidator(self.instance)(self.cleaned_data) | ||
| PluginLockedValidator(self.instance)(self.cleaned_data) | ||
| PluginPythonPathValidator(self.instance)(self.cleaned_data) | ||
|
|
||
|
|
||
| @admin.register(Plugin) | ||
| class PluginAdmin(admin.ModelAdmin): | ||
| form = PluginAdminForm | ||
|
|
||
| search_fields = ['uri', 'python_path', *get_language_fields('title'), *get_language_fields('help')] | ||
| list_display = ('uri', 'python_path', 'plugin_type', 'available') | ||
| readonly_fields = ('uri', 'plugin_type') | ||
| list_filter = ('available', 'python_path', 'sites' , 'groups', 'catalogs') | ||
| filter_horizontal = ('catalogs', 'sites', 'editors', 'groups') | ||
| ordering = ('python_path','order') | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| from django.apps import AppConfig | ||
| from django.utils.translation import gettext_lazy as _ | ||
|
|
||
|
|
||
| class ConfigConfig(AppConfig): | ||
| name = 'rdmo.config' | ||
| verbose_name = _('Config') | ||
|
|
||
| def ready(self): | ||
| from . import checks # noqa: F401 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| from collections import defaultdict | ||
|
|
||
| from django.core.checks import Warning, register | ||
|
|
||
|
|
||
| @register() | ||
| def deprecated_plugin_settings_check(app_configs, **kwargs): | ||
| from django.conf import settings | ||
| legacy_settings = [ | ||
| "PROJECT_EXPORTS", | ||
| "PROJECT_SNAPSHOT_EXPORTS", | ||
| "PROJECT_IMPORTS", | ||
| "PROJECT_ISSUE_PROVIDERS", | ||
| "PROJECT_IMPORTS_LIST", | ||
| "OPTIONSET_PROVIDERS", | ||
| ] | ||
| issues = [] | ||
| legacy_settings_used_keys = { | ||
| i for i in legacy_settings if hasattr(settings, i) | ||
| } | ||
| if legacy_settings_used_keys: | ||
| _verb = "are" if len(legacy_settings_used_keys) > 1 else "is" | ||
| legacy_settings = { | ||
| i: getattr(settings, i) for i in legacy_settings_used_keys | ||
| } | ||
| _legacy_settings_plugins = defaultdict(list) | ||
| _python_paths = set() | ||
| for name,entries in legacy_settings.items(): | ||
| for entry in entries: | ||
| _legacy_settings_plugins[name].append(entry) | ||
| if len(entry) == 3: | ||
| _python_paths.add(entry[-1]) | ||
| issues.append(Warning( | ||
| f"{', '.join(legacy_settings_used_keys)} {_verb} deprecated as of RDMO 2.5.0; " | ||
| f"use PLUGINS = ['python.dotted.paths', ...] instead.", | ||
| id="rdmo.config.W001", | ||
| hint="Define the legacy plugin settings in PLUGINS and remove the legacy settings." | ||
| f"\n{repr_new_settings(_python_paths)}", | ||
| )) | ||
| # If both PLUGINS and any legacy key exist | ||
| if hasattr(settings, "PLUGINS"): | ||
| issues.append(Warning( | ||
| "PLUGINS is set in addition to legacy settings; the legacy settings are ignored.", | ||
| id="rdmo.config.W002", | ||
| hint="Remove the following legacy settings to avoid confusion: " | ||
| f"{', '.join(legacy_settings_used_keys)}." | ||
| )) | ||
| return issues | ||
|
|
||
MyPyDavid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| def repr_new_settings(python_paths) -> str: | ||
| if not python_paths: | ||
| return "" | ||
| elif len(python_paths) == 1: | ||
| return f"PLUGINS = ['{python_paths}']" | ||
| msg = "PLUGINS = [" | ||
| for python_path in sorted(python_paths): | ||
| msg += f"\n\t{python_path}, " | ||
| msg += "\n]" | ||
| return msg | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from django.db.models import TextChoices | ||
|
|
||
|
|
||
| class PLUGIN_TYPES(TextChoices): | ||
| PROJECT_EXPORT = "project_export", "Project export" | ||
| PROJECT_SNAPSHOT_EXPORT = "project_snapshot_export", "Project snapshot export" | ||
| PROJECT_IMPORT = "project_import", "Project import" | ||
| PROJECT_ISSUE_PROVIDER = "project_issue_provider", "Project issue provider" | ||
| OPTIONSET_PROVIDER = "optionset_provider", "Optionset provider" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| from rdmo.core.import_helpers import ElementImportHelper, ExtraFieldHelper | ||
|
|
||
| from .models import Plugin | ||
| from .validators import PluginLockedValidator, PluginUniqueURIValidator | ||
|
|
||
| import_helper_plugin = ElementImportHelper( | ||
| model=Plugin, | ||
| validators=(PluginLockedValidator, PluginUniqueURIValidator), | ||
| lang_fields=('title', 'help'), | ||
| extra_fields=( | ||
| ExtraFieldHelper(field_name='python_path'), | ||
| ExtraFieldHelper(field_name='plugin_settings', overwrite_in_element=True), | ||
| ExtraFieldHelper(field_name='available', overwrite_in_element=True), | ||
| ExtraFieldHelper(field_name='locked'), | ||
| ExtraFieldHelper(field_name='order'), | ||
| ExtraFieldHelper(field_name='url_name'), | ||
|
|
||
| ), | ||
| add_current_site_sites = True, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| from django.conf import settings | ||
|
|
||
| from rdmo.config.constants import PLUGIN_TYPES | ||
|
|
||
| PLUGIN_TYPE_TO_SETTING_KEY = { | ||
| PLUGIN_TYPES.PROJECT_IMPORT: "PROJECT_IMPORTS", | ||
| PLUGIN_TYPES.PROJECT_EXPORT: "PROJECT_EXPORTS", | ||
| PLUGIN_TYPES.PROJECT_SNAPSHOT_EXPORT: "PROJECT_SNAPSHOT_EXPORTS", | ||
| PLUGIN_TYPES.PROJECT_ISSUE_PROVIDER: "PROJECT_ISSUE_PROVIDERS", | ||
| PLUGIN_TYPES.OPTIONSET_PROVIDER: "OPTIONSET_PROVIDERS", | ||
| } | ||
|
|
||
| def get_plugins_from_legacy_settings(select_plugin_type=None) -> list[dict]: | ||
| """Read 3-tuples (key, label, python-path) from legacy settings.""" | ||
| plugin_definitions: list[dict] = [] | ||
| for plugin_type, legacy_setting in PLUGIN_TYPE_TO_SETTING_KEY.items(): | ||
| if not hasattr(settings, legacy_setting): | ||
| continue | ||
| if select_plugin_type is not None and select_plugin_type != plugin_type: | ||
| continue | ||
|
|
||
| legacy_plugins = getattr(settings, legacy_setting, None) | ||
| if not legacy_plugins: | ||
| continue | ||
|
|
||
| for entry in legacy_plugins: | ||
| try: | ||
| key, label, dotted = entry | ||
| except ValueError as exc: | ||
| raise ValueError( | ||
| f"{legacy_setting} must be a sequence of 3-tuples " | ||
| f"(key, label, python-path); got {entry!r}" | ||
| ) from exc | ||
|
|
||
| plugin_definitions.append({ | ||
| "uri_prefix": settings.DEFAULT_URI_PREFIX, | ||
| "uri_path": f"{legacy_setting.lower()}/{key}", | ||
| "title": label, | ||
| "python_path": dotted, | ||
| "plugin_type": plugin_type, | ||
| "url_name": key, | ||
| }) | ||
|
|
||
| return plugin_definitions |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from django.core.management.base import BaseCommand | ||
| from django.utils.module_loading import import_string | ||
|
|
||
| from rdmo.config.models import Plugin | ||
|
|
||
|
|
||
| class Command(BaseCommand): | ||
| help = "Check that all configured plugins can be imported" | ||
|
|
||
| def handle(self, *args, **options): | ||
| if not Plugin.objects.exists(): | ||
| self.stdout.write(self.style.SUCCESS("No plugins found.")) | ||
|
|
||
| for plugin in Plugin.objects.order_by('python_path').all(): | ||
| try: | ||
| import_string(plugin.python_path) | ||
| url_name = f" url_name='{plugin.url_name}'" if plugin.url_name else "" | ||
| self.stdout.write(self.style.SUCCESS(f"✔ {plugin.python_path}, type={plugin.plugin_type}{url_name}.")) | ||
| except ImportError as e: | ||
| if plugin.available: | ||
| self.stdout.write(self.style.ERROR( | ||
| f"✖ {plugin.python_path}, type={plugin.plugin_type} failed: {e}") | ||
| ) | ||
| else: | ||
| self.stdout.write(self.style.WARNING( | ||
| f"!! {plugin.python_path}, type={plugin.plugin_type} (=unavailable) failed: {e}") | ||
| ) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.