add version contraints and cacheops config

This commit is contained in:
John Anderson 2020-03-18 18:28:27 -04:00
parent fd879c7cf5
commit c7fb2ff894
3 changed files with 45 additions and 18 deletions

View File

@ -5,7 +5,7 @@ import inspect
from django.core.exceptions import ImproperlyConfigured
from django.template.loader import get_template
from extras.utils import registry
from extras.registry import registry
from .signals import register_detail_page_content_classes, register_nav_menu_link_classes
@ -76,7 +76,7 @@ def register_content_classes():
"""
Helper method that populates the registry with all template content classes that have been registered by plugins
"""
registry.plugin_template_content_classes = collections.defaultdict(list)
registry['plugin_template_content_classes'] = collections.defaultdict(list)
responses = register_detail_page_content_classes.send('registration_event')
for receiver, response in responses:
@ -90,7 +90,7 @@ def register_content_classes():
if template_class.model is None:
raise TypeError('Plugin content class {} does not define a valid model!'.format(template_class))
registry.plugin_template_content_classes[template_class.model].append(template_class)
registry['plugin_template_content_classes'][template_class.model].append(template_class)
def get_content_classes(model):
@ -98,10 +98,10 @@ def get_content_classes(model):
Given a model string, return the list of all registered template content classes.
Populate the registry if it is empty.
"""
if not hasattr(registry, 'plugin_template_content_classes'):
if 'plugin_template_content_classes' not in registry:
register_content_classes()
return registry.plugin_template_content_classes.get(model, [])
return registry['plugin_template_content_classes'].get(model, [])
#
@ -139,7 +139,7 @@ def register_nav_menu_links():
"""
Helper method that populates the registry with all nav menu link classes that have been registered by plugins
"""
registry.plugin_nav_menu_link_classes = {}
registry['plugin_nav_menu_link_classes'] = {}
responses = register_nav_menu_link_classes.send('registration_event')
for receiver, response in responses:
@ -165,7 +165,7 @@ def register_nav_menu_links():
if not isinstance(button, PluginNavMenuButton):
raise TypeError('{} must be an instance of PluginNavMenuButton!'.format(button))
registry.plugin_nav_menu_link_classes[section_name] = response
registry['plugin_nav_menu_link_classes'][section_name] = response
def get_nav_menu_link_classes():
@ -173,7 +173,7 @@ def get_nav_menu_link_classes():
Return the list of all registered nav menu link classes.
Populate the registry if it is empty.
"""
if not hasattr(registry, 'plugin_nav_menu_link_classes'):
if 'plugin_nav_menu_link_classes' not in registry:
register_nav_menu_links()
return registry.plugin_nav_menu_link_classes
return registry['plugin_nav_menu_link_classes']

View File

@ -10,7 +10,7 @@ from urllib.parse import urlsplit
from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.validators import URLValidator
from pkg_resources import iter_entry_points
from pkg_resources import iter_entry_points, parse_version
#
@ -645,6 +645,7 @@ if PLUGINS_ENABLED:
PLUGINS.append(plugin)
INSTALLED_APPS.append(plugin)
# Import the app config and locate the inner meta class
try:
module = importlib.import_module(plugin)
default_app_config = getattr(module, 'default_app_config')
@ -659,18 +660,22 @@ if PLUGINS_ENABLED:
'The app config for plugin {} does not contain an inner meta class'.format(plugin)
)
# Check version contraints
min_version = getattr(app_config_meta, 'min_version', None)
max_version = getattr(app_config_meta, 'max_version', None)
parsed_min_version = parse_version(min_version or VERSION)
parsed_max_version = parse_version(max_version or VERSION)
if min_version and max_version and parsed_min_version > parsed_max_version:
raise ImproperlyConfigured('Plugin {} specifies invalid version contraints!'.format(plugin))
if min_version and parsed_min_version > parse_version(VERSION):
raise ImproperlyConfigured('Plugin {} requires NetBox minimum version {}!'.format(plugin, min_version))
if max_version and parsed_max_version < parse_version(VERSION):
raise ImproperlyConfigured('Plugin {} requires NetBox maximum version {}!'.format(plugin, max_version))
# Add middleware
plugin_middleware = getattr(app_config_meta, 'middleware', [])
if plugin_middleware and isinstance(plugin_middleware, list):
MIDDLEWARE.extend(plugin_middleware)
plugin_middleware_prepend = getattr(app_config_meta, 'middleware_prepend', [])
if plugin_middleware_prepend and isinstance(plugin_middleware_prepend, list):
MIDDLEWARE[:0] = plugin_middleware_prepend
# Add installed apps
plugin_installed_apps = getattr(app_config_meta, 'installed_apps', [])
if plugin_installed_apps and isinstance(plugin_installed_apps, list):
INSTALLED_APPS.extend(plugin_installed_apps)
# Verify required configuration settings
if plugin not in PLUGINS_CONFIG:
@ -688,3 +693,18 @@ if PLUGINS_ENABLED:
for setting, value in getattr(app_config_meta, 'default_settings', {}).items():
if setting not in PLUGINS_CONFIG[plugin]:
PLUGINS_CONFIG[plugin][setting] = value
# Apply cacheops config
plugin_cacheops = getattr(app_config_meta, 'caching_config', {})
if plugin_cacheops and isinstance(plugin_cacheops, dict):
for key in plugin_cacheops.keys():
# Validate config is only being set for the given plugin
try:
app = key.split('.')[0]
except IndexError:
raise ImproperlyConfigured('Plugin {} caching_config is invalid!'.format(plugin))
if app != plugin:
raise ImproperlyConfigured(
'Plugin {} may not modify caching config for another app!'.format(plugin)
)
CACHEOPS.update(plugin_cacheops)

View File

@ -75,26 +75,33 @@ _patterns = [
plugin_patterns = []
plugin_api_patterns = []
for app in apps.get_app_configs():
# Loop over all apps look for installed plugins
if hasattr(app, 'NetBoxPluginMeta'):
# Check if the plugin specifies any URLs
if importlib.util.find_spec('{}.urls'.format(app.name)):
urls = importlib.import_module('{}.urls'.format(app.name))
url_slug = getattr(app.NetBoxPluginMeta, 'url_slug', app.label)
if hasattr(urls, 'urlpatterns'):
# Mount URLs at `<url_slug>/<path>`
plugin_patterns.append(
path('{}/'.format(url_slug), include((urls.urlpatterns, app.label)))
)
# Check if the plugin specifies any API URLs
if importlib.util.find_spec('{}.api'.format(app.name)):
if importlib.util.find_spec('{}.api.urls'.format(app.name)):
urls = importlib.import_module('{}.api.urls'.format(app.name))
if hasattr(urls, 'urlpatterns'):
url_slug = getattr(app.NetBoxPluginMeta, 'url_slug', app.label)
# Mount URLs at `<url_slug>/<path>`
plugin_api_patterns.append(
path('{}/'.format(url_slug), include((urls.urlpatterns, app.label)))
)
# Mount all plugin URLs within the `plugins` namespace
_patterns.append(
path('plugins/', include((plugin_patterns, 'plugins')))
)
# Mount all plugin API URLs within the `plugins-api` namespace
_patterns.append(
path('api/plugins/', include((plugin_api_patterns, 'plugins-api')))
)