Skip to content

Commit

Permalink
Merge pull request #33830 from dimagi/gh/web-apps/display-ucr-count
Browse files Browse the repository at this point in the history
Display mobile UCR count to user
  • Loading branch information
gherceg committed Dec 13, 2023
2 parents eeed63e + b959e0f commit 365346d
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 45 deletions.
3 changes: 3 additions & 0 deletions corehq/apps/app_manager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
from corehq.apps.builds.models import BuildRecord, BuildSpec
from corehq.apps.builds.utils import get_default_build_spec
from corehq.apps.cleanup.models import DeletedCouchDoc
from corehq.apps.cloudcare.utils import get_mobile_ucr_count
from corehq.apps.domain.models import Domain
from corehq.apps.hqmedia.models import (
ApplicationMediaMixin,
Expand Down Expand Up @@ -4497,6 +4498,7 @@ def delete_app(self):
get_app_languages.clear(self.domain)
get_apps_in_domain.clear(self.domain, True)
get_apps_in_domain.clear(self.domain, False)
get_mobile_ucr_count.clear(self.domain)
self.doc_type += '-Deleted'
record = DeleteApplicationRecord(
domain=self.domain,
Expand All @@ -4523,6 +4525,7 @@ def save(self, response_json=None, increment_version=None, **params):
get_app_languages.clear(self.domain)
get_apps_in_domain.clear(self.domain, True)
get_apps_in_domain.clear(self.domain, False)
get_mobile_ucr_count.clear(self.domain)

request = view_utils.get_request()
user = getattr(request, 'couch_user', None)
Expand Down
63 changes: 46 additions & 17 deletions corehq/apps/cloudcare/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.test import TestCase

from corehq.apps.app_manager.models import Application, ReportModule, ReportAppConfig
from corehq.apps.cloudcare.utils import should_restrict_web_apps_usage
from corehq.apps.cloudcare.utils import get_mobile_ucr_count, should_restrict_web_apps_usage
from corehq.apps.domain.shortcuts import create_domain
from corehq.util.test_utils import flag_disabled, flag_enabled

Expand All @@ -11,58 +11,87 @@ class TestShouldRestrictWebAppsUsage(TestCase):
@flag_disabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_disabled("MOBILE_UCR")
def test_returns_false_if_domain_ucr_count_is_under_limit_and_neither_toggle_is_enable(self):
self._create_app_with_reports(report_count=0)
with self.settings(MAX_MOBILE_UCR_LIMIT=1):
result = should_restrict_web_apps_usage(self.domain.name)
result = should_restrict_web_apps_usage(self.domain, 0)
self.assertFalse(result)

@flag_disabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_disabled("MOBILE_UCR")
def test_returns_false_if_domain_ucr_count_exceeds_limit_and_neither_toggle_is_enabled(self):
self._create_app_with_reports(report_count=2)
with self.settings(MAX_MOBILE_UCR_LIMIT=1):
result = should_restrict_web_apps_usage(self.domain.name)
result = should_restrict_web_apps_usage(self.domain, 2)
self.assertFalse(result)

@flag_enabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_disabled("MOBILE_UCR")
def test_returns_false_if_domain_ucr_count_exceeds_limit_and_ALLOW_WEB_APPS_RESTRICTION_is_enabled(self):
self._create_app_with_reports(report_count=2)
with self.settings(MAX_MOBILE_UCR_LIMIT=1):
result = should_restrict_web_apps_usage(self.domain.name)
result = should_restrict_web_apps_usage(self.domain, 2)
self.assertFalse(result)

@flag_disabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_enabled("MOBILE_UCR")
def test_returns_false_if_domain_ucr_count_exceeds_limit_and_MOBILE_UCR_is_enabled(self):
self._create_app_with_reports(report_count=2)
with self.settings(MAX_MOBILE_UCR_LIMIT=1):
result = should_restrict_web_apps_usage(self.domain.name)
result = should_restrict_web_apps_usage(self.domain, 2)
self.assertFalse(result)

@flag_enabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_enabled("MOBILE_UCR")
def test_returns_false_if_domain_ucr_count_is_under_limit_and_both_flags_are_enabled(self):
self._create_app_with_reports(report_count=1)
with self.settings(MAX_MOBILE_UCR_LIMIT=2):
result = should_restrict_web_apps_usage(self.domain.name)
result = should_restrict_web_apps_usage(self.domain, 1)
self.assertFalse(result)

@flag_enabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_enabled("MOBILE_UCR")
def test_returns_false_if_domain_ucr_count_equals_limit_and_both_flags_are_enabled(self):
self._create_app_with_reports(report_count=1)
with self.settings(MAX_MOBILE_UCR_LIMIT=1):
result = should_restrict_web_apps_usage(self.domain.name)
result = should_restrict_web_apps_usage(self.domain, 1)
self.assertFalse(result)

@flag_enabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_enabled("MOBILE_UCR")
def test_returns_true_if_domain_ucr_count_exceeds_limit_and_both_flags_are_enabled(self):
self._create_app_with_reports(report_count=2)
with self.settings(MAX_MOBILE_UCR_LIMIT=1):
result = should_restrict_web_apps_usage(self.domain.name)
self.assertTrue(result)
result = should_restrict_web_apps_usage(self.domain, 2)
self.assertTrue(result)

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.domain = 'restrict-web-apps-test'


class TestGetMobileUCRCount(TestCase):

@flag_disabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_disabled("MOBILE_UCR")
def test_returns_zero_if_neither_toggle_is_enable(self):
self._create_app_with_reports(report_count=1)
count = get_mobile_ucr_count(self.domain.name)
self.assertEqual(count, 0)

@flag_enabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_disabled("MOBILE_UCR")
def test_returns_zero_if_ALLOW_WEB_APPS_RESTRICTION_is_enabled(self):
self._create_app_with_reports(report_count=1)
count = get_mobile_ucr_count(self.domain.name)
self.assertEqual(count, 0)

@flag_disabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_enabled("MOBILE_UCR")
def test_returns_zero_if_MOBILE_UCR_is_enabled(self):
self._create_app_with_reports(report_count=1)
count = get_mobile_ucr_count(self.domain.name)
self.assertEqual(count, 0)

@flag_enabled("ALLOW_WEB_APPS_RESTRICTION")
@flag_enabled("MOBILE_UCR")
def test_returns_ucr_count_if_both_flags_are_enabled(self):
self._create_app_with_reports(report_count=1)
count = get_mobile_ucr_count(self.domain.name)
self.assertEqual(count, 1)

def _create_app_with_reports(self, report_count=1):
configs = []
Expand All @@ -76,5 +105,5 @@ def _create_app_with_reports(self, report_count=1):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.domain = create_domain('test-web-apps-restriction')
cls.domain = create_domain('mobile-ucr-count-test')
cls.addClassCleanup(cls.domain.delete)
33 changes: 25 additions & 8 deletions corehq/apps/cloudcare/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from corehq import toggles
from corehq.apps.app_manager.dbaccessors import get_apps_in_domain
from corehq.util.quickcache import quickcache


def should_show_preview_app(request, app, username):
Expand Down Expand Up @@ -37,18 +38,18 @@ def webapps_module(domain, app_id, module_id):
return _webapps_url(domain, app_id, selections=[module_id])


def should_restrict_web_apps_usage(domain):
@quickcache(['domain'], timeout=24 * 60 * 60)
def get_mobile_ucr_count(domain):
"""
This check is only applicable to domains that have both the MOBILE_UCR and ALLOW_WEB_APPS_RESTRICTION
feature flags enabled.
Checks the number of UCRs referenced across all applications in a domain
:returns: True if the total number exceeds the limit set in settings.MAX_MOBILE_UCR_LIMIT
Obtains the count of UCRs referenced across all applications in the specificed domain
If the MOBILE_UCR feature flag is not enabled, returns zero
If the ALLOW_WEB_APPS_RESTRICTION is not enabled, returns zero
"""
if not toggles.MOBILE_UCR.enabled(domain):
return False
return 0

if not toggles.ALLOW_WEB_APPS_RESTRICTION.enabled(domain):
return False
return 0

apps = get_apps_in_domain(domain, include_remote=False)
ucrs = [
Expand All @@ -57,4 +58,20 @@ def should_restrict_web_apps_usage(domain):
for module in app.get_report_modules()
for ucr in module.report_configs
]
return len(ucrs) > settings.MAX_MOBILE_UCR_LIMIT
return len(ucrs)


def should_restrict_web_apps_usage(domain, ucr_count):
"""
This check is only applicable to domains that have both the MOBILE_UCR and ALLOW_WEB_APPS_RESTRICTION
feature flags enabled.
Given the number of UCRs referenced in applications across a domain, returns True if above
the MAX_MOBILE_UCR_LIMIT or False otherwise.
"""
if not toggles.MOBILE_UCR.enabled(domain):
return False

if not toggles.ALLOW_WEB_APPS_RESTRICTION.enabled(domain):
return False

return ucr_count > settings.MAX_MOBILE_UCR_LIMIT
42 changes: 22 additions & 20 deletions corehq/apps/cloudcare/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@
HttpResponseRedirect,
JsonResponse,
)
from django.shortcuts import (
redirect,
render
)
from django.shortcuts import redirect, render

from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.decorators import method_decorator
Expand Down Expand Up @@ -63,7 +61,7 @@
from corehq.apps.cloudcare.decorators import require_cloudcare_access
from corehq.apps.cloudcare.esaccessors import login_as_user_query
from corehq.apps.cloudcare.models import SQLAppGroup
from corehq.apps.cloudcare.utils import should_restrict_web_apps_usage
from corehq.apps.cloudcare.utils import get_mobile_ucr_count, should_restrict_web_apps_usage
from corehq.apps.domain.decorators import (
domain_admin_required,
login_and_domain_required,
Expand Down Expand Up @@ -170,7 +168,8 @@ def set_cookie(response):
return request.couch_user, set_cookie

def get(self, request, domain):
if should_restrict_web_apps_usage(domain):
mobile_ucr_count = get_mobile_ucr_count(domain)
if should_restrict_web_apps_usage(domain, mobile_ucr_count):
return redirect('block_web_apps', domain=domain)

option = request.GET.get('option')
Expand Down Expand Up @@ -303,8 +302,9 @@ class PreviewAppView(TemplateView):
@use_daterangepicker
@xframe_options_sameorigin
def get(self, request, *args, **kwargs):
if should_restrict_web_apps_usage(request.domain):
context = get_context_for_ucr_limit_error(request.domain)
mobile_ucr_count = get_mobile_ucr_count(request.domain)
if should_restrict_web_apps_usage(request.domain, mobile_ucr_count):
context = BlockWebAppsView.get_context_for_ucr_limit_error(request.domain, mobile_ucr_count)
return render(request, 'preview_app/block_app_preview.html', context)
app = get_app(request.domain, kwargs.pop('app_id'))
return self.render_to_response({
Expand Down Expand Up @@ -626,20 +626,22 @@ class BlockWebAppsView(BaseDomainView):
template_name = 'block_web_apps.html'

def get(self, request, *args, **kwargs):
context = get_context_for_ucr_limit_error(request.domain)
mobile_ucr_count = get_mobile_ucr_count(request.domain)
context = self.get_context_for_ucr_limit_error(request.domain, mobile_ucr_count)
return render(request, self.template_name, context)


def get_context_for_ucr_limit_error(domain):
return {
'domain': domain,
'ucr_limit': settings.MAX_MOBILE_UCR_LIMIT,
'error_message': _("""You have the MOBILE_UCR feature flag enabled, and have exceeded the maximum limit
of {ucr_limit} total User Configurable Reports used across all of your applications.
To resolve, you must remove references to UCRs in your applications until you are under
the limit. If you believe this is a mistake, please reach out to support.
""").format(ucr_limit=settings.MAX_MOBILE_UCR_LIMIT)
}
@staticmethod
def get_context_for_ucr_limit_error(domain, mobile_ucr_count):
return {
'domain': domain,
'ucr_limit': settings.MAX_MOBILE_UCR_LIMIT,
'error_message': _("""You have the MOBILE_UCR feature flag enabled, and have {ucr_count} mobile UCRs
which exceeds the maximum limit of {ucr_limit} total User Configurable Reports used
across all of your applications. To resolve, you must remove references to UCRs in
your applications until you are under the limit. If you believe this is a mistake,
please reach out to support.
""").format(ucr_count=mobile_ucr_count, ucr_limit=settings.MAX_MOBILE_UCR_LIMIT)
}


@login_and_domain_required
Expand Down

0 comments on commit 365346d

Please sign in to comment.