diff --git a/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py b/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py index d3ade4455512..61629f123387 100644 --- a/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py +++ b/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py @@ -1,12 +1,18 @@ import unittest + +from django.db.models import QuerySet from mock import patch from django.core.exceptions import ImproperlyConfigured, MultipleObjectsReturned -from django.test import TestCase, override_settings +from django.test import TestCase from django.test.client import RequestFactory from openedx.core.djangoapps.appsembler.api.tests.factories import OrganizationFactory -from openedx.core.djangoapps.appsembler.sites.utils import get_current_organization, get_initial_page_elements +from openedx.core.djangoapps.appsembler.sites.utils import ( + get_current_organization, + get_initial_page_elements, + get_active_sites, +) from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from organizations.models import Organization @@ -24,6 +30,44 @@ def test_initial_page_elements(self): }) +class ActiveSitesTestCase(TestCase): + def setUp(self): + super(ActiveSitesTestCase, self).setUp() + self.siteFoo = SiteFactory.create(domain='foo.dev', name='foo.dev') + self.siteBar = SiteFactory.create(domain='bar.dev', name='bar.dev') + self.organizationA = OrganizationFactory(sites=[self.siteFoo]) + self.organizationB = OrganizationFactory(sites=[self.siteBar]) + + def test_get_active_sites(self): + """ + Basic test for results. + """ + with patch('openedx.core.djangoapps.appsembler.sites.utils.get_active_organizations') as mocked: + mocked.return_value = [self.organizationA, self.organizationB] + active_sites = get_active_sites() + assert len(active_sites) == 2 + assert active_sites[0].domain == 'bar.dev' + assert active_sites[1].domain == 'foo.dev' + + def test_get_active_sites_queryset(self): + """ + Should return QuerySet to work well with ViewSets and other plugins. + """ + with patch('openedx.core.djangoapps.appsembler.sites.utils.get_active_organizations') as mocked: + mocked.return_value = [self.organizationA, self.organizationB] + active_sites = get_active_sites() + assert type(active_sites) == QuerySet + + def test_get_active_sites_ordering(self): + """ + Result ordering is useful for tests but it's worth testing it itself. + """ + with patch('openedx.core.djangoapps.appsembler.sites.utils.get_active_organizations') as mocked: + mocked.return_value = [self.organizationA, self.organizationB] + active_sites = get_active_sites('-domain') + assert active_sites[0].domain == 'foo.dev' + + class OrganizationByRequestTestCase(TestCase): def setUp(self): super(OrganizationByRequestTestCase, self).setUp() diff --git a/openedx/core/djangoapps/appsembler/sites/utils.py b/openedx/core/djangoapps/appsembler/sites/utils.py index cdc1bb36807b..c87fe4b48126 100644 --- a/openedx/core/djangoapps/appsembler/sites/utils.py +++ b/openedx/core/djangoapps/appsembler/sites/utils.py @@ -54,9 +54,9 @@ def get_site_by_organization(org): return org.sites.all()[0] -def get_active_organizations(): +def _get_active_tiers_uuids(): """ - Get active organizations based on Tiers information. + Get active Tier organiation UUIDs from the Tiers (AMC Postgres) database. Note: This mostly a hack that's needed for improving the performance of batch operations by excluding dead sites. @@ -65,20 +65,33 @@ def get_active_organizations(): """ from tiers.models import Tier # This queries the AMC Postgres database - active_tiers = Tier.objects.filter( + active_tiers_uuids = Tier.objects.filter( Q(tier_enforcement_exempt=True) | Q(tier_expires_at__gte=timezone.now()) ).annotate( organization_edx_uuid=F('organization__edx_uuid') ).values_list('organization_edx_uuid', flat=True) + return active_tiers_uuids + + +def get_active_organizations(): + """ + Get active organizations based on Tiers information. + + Note: This mostly a hack that's needed for improving the performance of + batch operations by excluding dead sites. + + TODO: This helper should live in a future Tahoe Sites package. + """ + active_tiers_uuids = _get_active_tiers_uuids() # Now back to the LMS MySQL database return Organization.objects.filter( - edx_uuid__in=[str(edx_uuid) for edx_uuid in active_tiers], + edx_uuid__in=[str(edx_uuid) for edx_uuid in active_tiers_uuids], ) -def get_active_sites(): +def get_active_sites(order_by='domain'): """ Get active sites based on Tiers information. @@ -87,10 +100,9 @@ def get_active_sites(): TODO: This helper should live in a future Tahoe Sites package. """ - sites = [] - for organization in get_active_organizations(): - sites.extend(organization.sites.all()) - return sites + return Site.objects.filter( + organizations__in=get_active_organizations() + ).order_by(order_by) @beeline.traced(name="get_amc_oauth_client")