This commit is contained in:
Ariel Rin 2022-07-07 18:07:49 +10:00
commit 236c70316c
17 changed files with 103 additions and 65 deletions

View File

@ -5,7 +5,7 @@ from typing import List, Optional
from pytz import utc from pytz import utc
from redis import Redis, RedisError from redis import Redis, RedisError
from django_redis import get_redis_connection from allianceauth.utils.cache import get_redis_client
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -39,7 +39,7 @@ class EventSeries:
_ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES" _ROOT_KEY = "ALLIANCEAUTH_EVENT_SERIES"
def __init__(self, key_id: str, redis: Redis = None) -> None: def __init__(self, key_id: str, redis: Redis = None) -> None:
self._redis = get_redis_connection("default") if not redis else redis self._redis = get_redis_client() if not redis else redis
try: try:
if not self._redis.ping(): if not self._redis.ping():
raise RuntimeError() raise RuntimeError()

View File

@ -18,7 +18,7 @@ MODULE_PATH = "allianceauth.authentication.task_statistics.event_series"
class TestEventSeries(TestCase): class TestEventSeries(TestCase):
def test_should_abort_without_redis_client(self): def test_should_abort_without_redis_client(self):
# when # when
with patch(MODULE_PATH + ".get_redis_connection") as mock: with patch(MODULE_PATH + ".get_redis_client") as mock:
mock.return_value = None mock.return_value = None
events = EventSeries("dummy") events = EventSeries("dummy")
# then # then
@ -27,8 +27,8 @@ class TestEventSeries(TestCase):
def test_should_disable_itself_if_redis_not_available_1(self): def test_should_disable_itself_if_redis_not_available_1(self):
# when # when
with patch(MODULE_PATH + ".get_redis_connection") as mock_get_redis_connection: with patch(MODULE_PATH + ".get_redis_client") as mock_get_master_client:
mock_get_redis_connection.return_value.ping.side_effect = RedisError mock_get_master_client.return_value.ping.side_effect = RedisError
events = EventSeries("dummy") events = EventSeries("dummy")
# then # then
self.assertIsInstance(events._redis, _RedisStub) self.assertIsInstance(events._redis, _RedisStub)
@ -36,8 +36,8 @@ class TestEventSeries(TestCase):
def test_should_disable_itself_if_redis_not_available_2(self): def test_should_disable_itself_if_redis_not_available_2(self):
# when # when
with patch(MODULE_PATH + ".get_redis_connection") as mock_get_redis_connection: with patch(MODULE_PATH + ".get_redis_client") as mock_get_master_client:
mock_get_redis_connection.return_value.ping.return_value = False mock_get_master_client.return_value.ping.return_value = False
events = EventSeries("dummy") events = EventSeries("dummy")
# then # then
self.assertIsInstance(events._redis, _RedisStub) self.assertIsInstance(events._redis, _RedisStub)

View File

@ -8,7 +8,7 @@ from uuid import uuid1
from redis import Redis from redis import Redis
import requests import requests
from django_redis import get_redis_connection from allianceauth.utils.cache import get_redis_client
from allianceauth import __title__ as AUTH_TITLE, __url__, __version__ from allianceauth import __title__ as AUTH_TITLE, __url__, __version__
@ -103,7 +103,7 @@ class DiscordClient:
self._access_token = str(access_token) self._access_token = str(access_token)
self._is_rate_limited = bool(is_rate_limited) self._is_rate_limited = bool(is_rate_limited)
if not redis: if not redis:
self._redis = get_redis_connection("default") self._redis = get_redis_client()
if not isinstance(self._redis, Redis): if not isinstance(self._redis, Redis):
raise RuntimeError( raise RuntimeError(
'This class requires a Redis client, but none was provided ' 'This class requires a Redis client, but none was provided '

View File

@ -85,25 +85,18 @@ class TestBasicsAndHelpers(TestCase):
client = DiscordClient(TEST_BOT_TOKEN, mock_redis, is_rate_limited=True) client = DiscordClient(TEST_BOT_TOKEN, mock_redis, is_rate_limited=True)
self.assertTrue(client.is_rate_limited) self.assertTrue(client.is_rate_limited)
@patch(MODULE_PATH + '.get_redis_connection') def test_use_default_redis_if_none_provided(self):
def test_use_default_redis_if_none_provided(self, mock_caches):
my_redis = MagicMock(spec=Redis)
mock_caches.return_value = my_redis
client = DiscordClient(TEST_BOT_TOKEN) client = DiscordClient(TEST_BOT_TOKEN)
self.assertTrue(mock_caches.called) self.assertIsInstance(client._redis, Redis)
self.assertEqual(client._redis, my_redis)
@patch(MODULE_PATH + '.get_redis_connection')
def test_raise_exception_if_default_cache_is_not_redis(self, mock_caches):
my_redis = MagicMock()
mock_caches.return_value = my_redis
@patch(MODULE_PATH + '.get_redis_client')
def test_raise_exception_if_redis_client_not_found(self, mock_get_redis_client):
# given
mock_get_redis_client.return_value = None
# when
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
DiscordClient(TEST_BOT_TOKEN) DiscordClient(TEST_BOT_TOKEN)
self.assertTrue(mock_caches.called)
@requests_mock.Mocker() @requests_mock.Mocker()
class TestOtherMethods(TestCase): class TestOtherMethods(TestCase):

View File

@ -35,17 +35,17 @@ import logging
from uuid import uuid1 from uuid import uuid1
import random import random
from django.core.cache import caches
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from allianceauth.services.modules.discord.models import DiscordUser from allianceauth.services.modules.discord.models import DiscordUser
from allianceauth.utils.cache import get_redis_client
logger = logging.getLogger('allianceauth') logger = logging.getLogger('allianceauth')
MAX_RUNS = 3 MAX_RUNS = 3
def clear_cache(): def clear_cache():
default_cache = caches['default'] redis = get_redis_client()
redis = default_cache.get_master_client()
redis.flushall() redis.flushall()
logger.info('Cache flushed') logger.info('Cache flushed')

View File

@ -14,7 +14,6 @@ from requests.exceptions import HTTPError
import requests_mock import requests_mock
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django_redis import get_redis_connection
from django.shortcuts import reverse from django.shortcuts import reverse
from django.test import TransactionTestCase, TestCase from django.test import TransactionTestCase, TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
@ -23,6 +22,7 @@ from allianceauth.authentication.models import State
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
from allianceauth.notifications.models import Notification from allianceauth.notifications.models import Notification
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.utils.cache import get_redis_client
from . import ( from . import (
TEST_GUILD_ID, TEST_GUILD_ID,
@ -87,7 +87,7 @@ remove_guild_member_request = DiscordRequest(
def clear_cache(): def clear_cache():
redis = get_redis_connection('default') redis = get_redis_client()
redis.flushall() redis.flushall()
logger.info('Cache flushed') logger.info('Cache flushed')
@ -108,7 +108,6 @@ def reset_testdata():
class TestServiceFeatures(TransactionTestCase): class TestServiceFeatures(TransactionTestCase):
fixtures = ['disable_analytics.json'] fixtures = ['disable_analytics.json']
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()

View File

View File

@ -0,0 +1,21 @@
from redis import Redis
from django.core.cache import caches
try:
import django_redis
except ImportError:
django_redis = None
def get_redis_client() -> Redis:
"""Get the configured redis client used by Django for caching.
This function is a wrapper designed to work for both AA2 and AA3
and should always be used to ensure backwards compatibility.
"""
try:
return django_redis.get_redis_connection("default")
except AttributeError:
default_cache = caches["default"]
return default_cache.get_master_client()

View File

View File

@ -0,0 +1,39 @@
from unittest.mock import MagicMock, patch
from django.test import TestCase
from allianceauth.utils.cache import get_redis_client
MODULE_PATH = "allianceauth.utils.cache"
class RedisClientStub:
"""Substitute for a Redis client."""
pass
class TestGetRedisClient(TestCase):
def test_should_work_with_aa2_api(self):
# given
mock_django_cache = MagicMock()
mock_django_cache.get_master_client.return_value = RedisClientStub()
# when
with patch(MODULE_PATH + ".django_redis", None), patch.dict(
MODULE_PATH + ".caches", {"default": mock_django_cache}
):
client = get_redis_client()
# then
self.assertIsInstance(client, RedisClientStub)
def test_should_work_with_aa3_api(self):
# given
mock_django_redis = MagicMock()
mock_django_redis.get_redis_connection.return_value = RedisClientStub()
# when
with patch(MODULE_PATH + ".django_redis", mock_django_redis), patch.dict(
MODULE_PATH + ".caches", {"default": None}
):
client = get_redis_client()
# then
self.assertIsInstance(client, RedisClientStub)

View File

@ -44,6 +44,8 @@ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.napoleon', 'sphinx.ext.napoleon',
'recommonmark', 'recommonmark',
'sphinxcontrib_django2',
'sphinx.ext.viewcode',
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.

View File

@ -4,36 +4,4 @@ django-esi
The django-esi package provides an interface for easy access to the ESI. The django-esi package provides an interface for easy access to the ESI.
Location: ``esi`` This is an external package. Please see `here <https://django-esi.readthedocs.io/en/latest/>`_ for it's documentation.
This is an external package. Please also see `here <https://gitlab.com/allianceauth/django-esi>`_ for it's official documentation.
clients
===========
.. automodule:: esi.clients
:members: esi_client_factory
:undoc-members:
decorators
===========
.. automodule:: esi.decorators
:members:
:undoc-members:
errors
===========
.. automodule:: esi.errors
:members:
:undoc-members:
models
===========
.. automodule:: esi.models
:members: Scope, Token
:exclude-members: objects, provider
:undoc-members:

View File

@ -11,5 +11,4 @@ models
.. automodule:: allianceauth.eveonline.models .. automodule:: allianceauth.eveonline.models
:members: :members:
:exclude-members: objects, provider :exclude-members: DoesNotExist, MultipleObjectsReturned
:undoc-members:

View File

@ -11,4 +11,5 @@ To reduce redundancy and help speed up development we encourage developers to ut
eveonline eveonline
notifications notifications
testutils testutils
utils
``` ```

View File

@ -13,7 +13,8 @@ models
=========== ===========
.. autoclass:: allianceauth.notifications.models.Notification .. autoclass:: allianceauth.notifications.models.Notification
:members: Level, mark_viewed, set_level :members:
:exclude-members: DoesNotExist, MultipleObjectsReturned, save
managers managers
=========== ===========

View File

@ -0,0 +1,14 @@
=============================
utils
=============================
Utilities and helper functions.
Location: ``allianceauth.utils``
cache
===========
.. automodule:: allianceauth.utils.cache
:members:
:undoc-members:

View File

@ -4,6 +4,7 @@ sphinx_rtd_theme>=1.0.0,<2.0.0
recommonmark==0.7.1 recommonmark==0.7.1
Jinja2<3.1 Jinja2<3.1
docutils==0.16 docutils==0.16
sphinxcontrib-django2
# Autodoc dependencies # Autodoc dependencies
celery>=5.2.0,<6.0.0 celery>=5.2.0,<6.0.0