From 61ec03e540af71d3fdb15099325e58c48da53fbd Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Tue, 9 Apr 2024 13:42:35 -0400 Subject: [PATCH] Move named url init out of Middleware init * Middleware classes can be instantiated multiple times in testing. To make this a non-issue, move the init code for named urls out of the middleware init and into the app init. * This makes it easier to use other testing facilities, like LiveServerTestCase, without having to mock the named url middleware init. --- awx/main/apps.py | 33 +++++++++++++++ awx/main/middleware.py | 47 --------------------- awx/main/tests/functional/test_named_url.py | 22 ---------- awx/main/utils/named_url_graph.py | 19 ++++++++- 4 files changed, 51 insertions(+), 70 deletions(-) diff --git a/awx/main/apps.py b/awx/main/apps.py index 7db39ca088..099caea96a 100644 --- a/awx/main/apps.py +++ b/awx/main/apps.py @@ -1,7 +1,40 @@ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ +from awx.main.utils.named_url_graph import _customize_graph, generate_graph +from awx.conf import register, fields class MainConfig(AppConfig): name = 'awx.main' verbose_name = _('Main') + + def load_named_url_feature(self): + models = [m for m in self.get_models() if hasattr(m, 'get_absolute_url')] + generate_graph(models) + _customize_graph() + register( + 'NAMED_URL_FORMATS', + field_class=fields.DictField, + read_only=True, + label=_('Formats of all available named urls'), + help_text=_('Read-only list of key-value pairs that shows the standard format of all available named URLs.'), + category=_('Named URL'), + category_slug='named-url', + ) + register( + 'NAMED_URL_GRAPH_NODES', + field_class=fields.DictField, + read_only=True, + label=_('List of all named url graph nodes.'), + help_text=_( + 'Read-only list of key-value pairs that exposes named URL graph topology.' + ' Use this list to programmatically generate named URLs for resources' + ), + category=_('Named URL'), + category_slug='named-url', + ) + + def ready(self): + super().ready() + + self.load_named_url_feature() diff --git a/awx/main/middleware.py b/awx/main/middleware.py index 647787e8a9..b5c39b03a3 100644 --- a/awx/main/middleware.py +++ b/awx/main/middleware.py @@ -9,18 +9,13 @@ from pathlib import Path from django.conf import settings from django.contrib.auth import logout -from django.contrib.auth.models import User from django.db.migrations.recorder import MigrationRecorder from django.db import connection from django.shortcuts import redirect -from django.apps import apps from django.utils.deprecation import MiddlewareMixin -from django.utils.translation import gettext_lazy as _ from django.urls import reverse, resolve from awx.main import migrations -from awx.main.utils.named_url_graph import generate_graph, GraphNode -from awx.conf import fields, register from awx.main.utils.profiling import AWXProfiler from awx.main.utils.common import memoize @@ -100,49 +95,7 @@ class DisableLocalAuthMiddleware(MiddlewareMixin): logout(request) -def _customize_graph(): - from awx.main.models import Instance, Schedule, UnifiedJobTemplate - - for model in [Schedule, UnifiedJobTemplate]: - if model in settings.NAMED_URL_GRAPH: - settings.NAMED_URL_GRAPH[model].remove_bindings() - settings.NAMED_URL_GRAPH.pop(model) - if User not in settings.NAMED_URL_GRAPH: - settings.NAMED_URL_GRAPH[User] = GraphNode(User, ['username'], []) - settings.NAMED_URL_GRAPH[User].add_bindings() - if Instance not in settings.NAMED_URL_GRAPH: - settings.NAMED_URL_GRAPH[Instance] = GraphNode(Instance, ['hostname'], []) - settings.NAMED_URL_GRAPH[Instance].add_bindings() - - class URLModificationMiddleware(MiddlewareMixin): - def __init__(self, get_response): - models = [m for m in apps.get_app_config('main').get_models() if hasattr(m, 'get_absolute_url')] - generate_graph(models) - _customize_graph() - register( - 'NAMED_URL_FORMATS', - field_class=fields.DictField, - read_only=True, - label=_('Formats of all available named urls'), - help_text=_('Read-only list of key-value pairs that shows the standard format of all available named URLs.'), - category=_('Named URL'), - category_slug='named-url', - ) - register( - 'NAMED_URL_GRAPH_NODES', - field_class=fields.DictField, - read_only=True, - label=_('List of all named url graph nodes.'), - help_text=_( - 'Read-only list of key-value pairs that exposes named URL graph topology.' - ' Use this list to programmatically generate named URLs for resources' - ), - category=_('Named URL'), - category_slug='named-url', - ) - super().__init__(get_response) - @staticmethod def _hijack_for_old_jt_name(node, kwargs, named_url): try: diff --git a/awx/main/tests/functional/test_named_url.py b/awx/main/tests/functional/test_named_url.py index 884ecd7dc0..54e3b96edd 100644 --- a/awx/main/tests/functional/test_named_url.py +++ b/awx/main/tests/functional/test_named_url.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from unittest import mock - import pytest -from django.core.exceptions import ImproperlyConfigured from django.conf import settings from awx.api.versioning import reverse @@ -23,25 +20,6 @@ from awx.main.models import ( # noqa User, WorkflowJobTemplate, ) -from awx.conf import settings_registry - - -def setup_module(module): - # In real-world scenario, named url graph structure is populated by __init__ - # of URLModificationMiddleware. The way Django bootstraps ensures the initialization - # will happen *once and only once*, while the number of initialization is uncontrollable - # in unit test environment. So it is wrapped by try-except block to mute any - # unwanted exceptions. - try: - URLModificationMiddleware(mock.Mock()) - except ImproperlyConfigured: - pass - - -def teardown_module(module): - # settings_registry will be persistent states unless we explicitly clean them up. - settings_registry.unregister('NAMED_URL_FORMATS') - settings_registry.unregister('NAMED_URL_GRAPH_NODES') @pytest.mark.django_db diff --git a/awx/main/utils/named_url_graph.py b/awx/main/utils/named_url_graph.py index 9d2c0a27c9..632064f0c1 100644 --- a/awx/main/utils/named_url_graph.py +++ b/awx/main/utils/named_url_graph.py @@ -5,7 +5,6 @@ from collections import deque # Django from django.db import models from django.conf import settings -from django.contrib.contenttypes.models import ContentType NAMED_URL_RES_DILIMITER = "++" @@ -245,6 +244,8 @@ def _generate_configurations(nodes): def _dfs(configuration, model, graph, dead_ends, new_deadends, parents): + from django.contrib.contenttypes.models import ContentType + parents.add(model) fields, fk_names = configuration[model][0][:], configuration[model][1][:] adj_list = [] @@ -306,3 +307,19 @@ def generate_graph(models): def reset_counters(): for node in settings.NAMED_URL_GRAPH.values(): node.counter = 0 + + +def _customize_graph(): + from django.contrib.auth.models import User + from awx.main.models import Instance, Schedule, UnifiedJobTemplate + + for model in [Schedule, UnifiedJobTemplate]: + if model in settings.NAMED_URL_GRAPH: + settings.NAMED_URL_GRAPH[model].remove_bindings() + settings.NAMED_URL_GRAPH.pop(model) + if User not in settings.NAMED_URL_GRAPH: + settings.NAMED_URL_GRAPH[User] = GraphNode(User, ['username'], []) + settings.NAMED_URL_GRAPH[User].add_bindings() + if Instance not in settings.NAMED_URL_GRAPH: + settings.NAMED_URL_GRAPH[Instance] = GraphNode(Instance, ['hostname'], []) + settings.NAMED_URL_GRAPH[Instance].add_bindings()