From 164a12e5d7de1ba9e32be99a71ccd72095aeca35 Mon Sep 17 00:00:00 2001 From: abram axel booth Date: Mon, 15 Jul 2024 10:01:51 -0400 Subject: [PATCH] fix: waffle flags in flask context (#10664) * fix: waffle flags in flask context --- addons/base/views.py | 5 ++-- api/institutions/authentication.py | 4 ++-- api/waffle/utils.py | 19 ++++++++------- osf/external/messages/celery_publishers.py | 5 ++-- osf/models/mixins.py | 7 +++--- tests/test_ember_osf_web.py | 4 ++-- website/ember_osf_web/decorators.py | 4 ++-- website/oauth/views.py | 4 ++-- website/project/views/file.py | 6 ++--- website/views.py | 27 +++++++++++----------- 10 files changed, 45 insertions(+), 40 deletions(-) diff --git a/addons/base/views.py b/addons/base/views.py index ecc17b778443..2e061f4f6657 100644 --- a/addons/base/views.py +++ b/addons/base/views.py @@ -24,6 +24,7 @@ from addons.osfstorage.models import OsfStorageFileNode from addons.osfstorage.utils import enqueue_update_analytics +from api.waffle.utils import flag_is_active from framework import sentry from framework.auth import Auth from framework.auth import cas @@ -869,10 +870,10 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): # There's no download action redirect to the Ember front-end file view and create guid. if action != 'download': - if isinstance(target, Node) and waffle.flag_is_active(request, features.EMBER_FILE_PROJECT_DETAIL): + if isinstance(target, Node) and flag_is_active(request, features.EMBER_FILE_PROJECT_DETAIL): guid = file_node.get_guid(create=True) return redirect(f'{settings.DOMAIN}{guid._id}/') - if isinstance(target, Registration) and waffle.flag_is_active(request, features.EMBER_FILE_REGISTRATION_DETAIL): + if isinstance(target, Registration) and flag_is_active(request, features.EMBER_FILE_REGISTRATION_DETAIL): guid = file_node.get_guid(create=True) return redirect(f'{settings.DOMAIN}{guid._id}/') diff --git a/api/institutions/authentication.py b/api/institutions/authentication.py index f475db8860cc..0370201e0770 100644 --- a/api/institutions/authentication.py +++ b/api/institutions/authentication.py @@ -4,7 +4,6 @@ import jwe import jwt -import waffle from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed, PermissionDenied @@ -14,6 +13,7 @@ from api.base.authentication import drf from api.base import exceptions, settings +from api.waffle.utils import flag_is_active from framework import sentry from framework.auth import get_or_create_institutional_user @@ -339,7 +339,7 @@ def authenticate(self, request): user=user, domain=DOMAIN, osf_support_email=OSF_SUPPORT_EMAIL, - storage_flag_is_active=waffle.flag_is_active(request, features.STORAGE_I18N), + storage_flag_is_active=flag_is_active(request, features.STORAGE_I18N), ) # Add the email to the user's account if it is identified by the eppn diff --git a/api/waffle/utils.py b/api/waffle/utils.py index 2370f87d8ff9..ae5b352a4ae5 100644 --- a/api/waffle/utils.py +++ b/api/waffle/utils.py @@ -2,7 +2,7 @@ from framework.auth.core import _get_current_user from osf import features -from flask import request +import flask class MockUser: @@ -13,18 +13,21 @@ def flag_is_active(request, flag_name): """ This function changes the typical flask request object so it can be used by django-waffle. Other modifications for django-waffle can be found in the __call__ method of OsfWebRenderer. - :param flask request object: - :return flask request object: + + :param request (flask or django): + :param flag name (string): + :return bool: """ - # Waffle does not enjoy NoneTypes as user values. - request.user = _get_current_user() or MockUser() - request.COOKIES = getattr(request, 'cookies', None) + if isinstance(request, flask.Request): + # Waffle does not enjoy NoneTypes as user values. + request.user = _get_current_user() or MockUser() + request.COOKIES = getattr(request, 'cookies', None) return waffle.flag_is_active(request, flag_name) def storage_i18n_flag_active(): - return flag_is_active(request, features.STORAGE_I18N) + return flag_is_active(flask.request, features.STORAGE_I18N) def storage_usage_flag_active(): - return flag_is_active(request, features.STORAGE_USAGE) + return flag_is_active(flask.request, features.STORAGE_USAGE) diff --git a/osf/external/messages/celery_publishers.py b/osf/external/messages/celery_publishers.py index 3c3c90f9d37e..7ac04cd48bd3 100644 --- a/osf/external/messages/celery_publishers.py +++ b/osf/external/messages/celery_publishers.py @@ -1,5 +1,6 @@ -import waffle from kombu import Exchange + +from api.waffle.utils import flag_is_active from framework.celery_tasks import app as celery_app from website import settings from osf import features @@ -36,7 +37,7 @@ def publish_merged_user(user): def _publish_user_status_change(body: dict): - if settings.USE_CELERY and waffle.flag_is_active(get_current_request(), features.ENABLE_GV): + if settings.USE_CELERY and flag_is_active(get_current_request(), features.ENABLE_GV): with celery_app.producer_pool.acquire() as producer: producer.publish( body=body, diff --git a/osf/models/mixins.py b/osf/models/mixins.py index e8eeb3dd5151..9328ec70b0ba 100644 --- a/osf/models/mixins.py +++ b/osf/models/mixins.py @@ -3,8 +3,6 @@ import markupsafe import logging -import waffle - from django.apps import apps from django.contrib.auth.models import Group, AnonymousUser from django.core.exceptions import ObjectDoesNotExist, ValidationError @@ -14,6 +12,7 @@ from guardian.shortcuts import assign_perm, get_perms, remove_perm, get_group_perms from api.providers.workflows import Workflows, PUBLIC_STATES +from api.waffle.utils import flag_is_active from framework import status from framework.auth import Auth from framework.auth.core import get_user @@ -491,7 +490,7 @@ def addons(self): def get_addons(self): request, user_id = get_request_and_user_id() - if waffle.flag_is_active(request, features.ENABLE_GV): + if flag_is_active(request, features.ENABLE_GV): osf_addons = filter( lambda x: x is not None, (self.get_addon(addon) for addon in self.OSF_HOSTED_ADDONS) @@ -528,7 +527,7 @@ def get_addon(self, name, is_deleted=False): # Avoid test-breakages by avoiding early access to the request context if name not in self.OSF_HOSTED_ADDONS: request, user_id = get_request_and_user_id() - if waffle.flag_is_active(request, features.ENABLE_GV): + if flag_is_active(request, features.ENABLE_GV): return self._get_addon_from_gv(gv_pk=name, requesting_user_id=user_id) try: diff --git a/tests/test_ember_osf_web.py b/tests/test_ember_osf_web.py index 0fc34a787f3b..f25ac1af961e 100644 --- a/tests/test_ember_osf_web.py +++ b/tests/test_ember_osf_web.py @@ -31,7 +31,7 @@ def test_dont_use_ember_app(self, mock_use_ember_app): assert not mock_use_ember_app.called @mock.patch('api.waffle.utils._get_current_user') - @mock.patch('website.ember_osf_web.decorators.waffle.flag_is_active') + @mock.patch('website.ember_osf_web.decorators.flag_is_active') @mock.patch('website.ember_osf_web.decorators.use_ember_app') def test_ember_flag_is_active_authenticated_user(self, mock_use_ember_app, mock_flag_is_active, mock__get_current_user): # mock over external module 'waflle.flag_is_active` not ours @@ -45,7 +45,7 @@ def test_ember_flag_is_active_authenticated_user(self, mock_use_ember_app, mock_ mock_use_ember_app.assert_called_with() @mock.patch('api.waffle.utils._get_current_user', return_value=None) - @mock.patch('website.ember_osf_web.decorators.waffle.flag_is_active') + @mock.patch('website.ember_osf_web.decorators.flag_is_active') @mock.patch('website.ember_osf_web.decorators.use_ember_app') def test_ember_flag_is_active_unauthenticated_user(self, mock_use_ember_app, mock_flag_is_active, mock__get_current_user): # mock over external module 'waflle.flag_is_active` not ours diff --git a/website/ember_osf_web/decorators.py b/website/ember_osf_web/decorators.py index 43780f7e4c40..978445f2bf38 100644 --- a/website/ember_osf_web/decorators.py +++ b/website/ember_osf_web/decorators.py @@ -1,8 +1,8 @@ -import waffle import functools from flask import request +from api.waffle.utils import flag_is_active from website.ember_osf_web.views import use_ember_app @@ -14,7 +14,7 @@ def ember_flag_is_active(flag_name): def decorator(func): @functools.wraps(func) def wrapped(*args, **kwargs): - if waffle.flag_is_active(request, flag_name): + if flag_is_active(request, flag_name): return use_ember_app() else: return func(*args, **kwargs) diff --git a/website/oauth/views.py b/website/oauth/views.py index 357869e4ecf3..bbacab894ce0 100644 --- a/website/oauth/views.py +++ b/website/oauth/views.py @@ -1,5 +1,4 @@ from rest_framework import status as http_status -import waffle import requests from urllib.parse import ( urlencode, @@ -9,6 +8,7 @@ from flask import redirect, request +from api.waffle.utils import flag_is_active from framework.auth.decorators import must_be_logged_in from framework.exceptions import HTTPError from osf.models import ExternalAccount @@ -48,7 +48,7 @@ def oauth_connect(service_name, auth): @must_be_logged_in def oauth_callback(service_name, auth): - if waffle.flag_is_active(request, features.ENABLE_GV): + if flag_is_active(request, features.ENABLE_GV): _forward_to_addon_service() return {} diff --git a/website/project/views/file.py b/website/project/views/file.py index 9459b78bf679..56da17187659 100644 --- a/website/project/views/file.py +++ b/website/project/views/file.py @@ -1,12 +1,12 @@ """ Files views. """ -import waffle from flask import request from osf import features from osf.models import Node, Registration +from api.waffle.utils import flag_is_active from website.util import rubeus from website.project.decorators import must_be_contributor_or_public, must_not_be_retracted_registration from website.project.views.node import _view_project @@ -19,10 +19,10 @@ def collect_file_trees(auth, node, **kwargs): """Collect file trees for all add-ons implementing HGrid views, then format data as appropriate. """ - if isinstance(node, Node) and waffle.flag_is_active(request, features.EMBER_PROJECT_FILES): + if isinstance(node, Node) and flag_is_active(request, features.EMBER_PROJECT_FILES): return use_ember_app() - if isinstance(node, Registration) and waffle.flag_is_active(request, features.EMBER_REGISTRATION_FILES): + if isinstance(node, Registration) and flag_is_active(request, features.EMBER_REGISTRATION_FILES): return use_ember_app() serialized = _view_project(node, auth, primary=True) diff --git a/website/views.py b/website/views.py index 2d654a81946f..de61c5548e61 100644 --- a/website/views.py +++ b/website/views.py @@ -1,12 +1,13 @@ -from furl import furl -import waffle +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +import furl import itertools from rest_framework import status as http_status import logging import math import os import requests -from urllib.parse import unquote +from future.moves.urllib.parse import unquote from django.apps import apps from flask import request, send_from_directory, Response, stream_with_context @@ -33,7 +34,7 @@ from osf.utils import permissions from osf.metadata.tools import pls_gather_metadata_file -from api.waffle.utils import storage_i18n_flag_active +from api.waffle.utils import storage_i18n_flag_active, flag_is_active logger = logging.getLogger(__name__) ember_osf_web_dir = os.path.abspath(os.path.join(os.getcwd(), EXTERNAL_EMBER_APPS['ember_osf_web']['path'])) @@ -265,7 +266,7 @@ def _build_guid_url(base, suffix=None): ]) if not isinstance(url, str): url = url.decode('utf-8') - return f'/{url}/' + return u'/{0}/'.format(url) def resolve_guid(guid, suffix=None): @@ -331,25 +332,25 @@ def resolve_guid(guid, suffix=None): return redirect(resource.absolute_url, http_status.HTTP_301_MOVED_PERMANENTLY) return use_ember_app() - elif isinstance(resource, Registration) and (clean_suffix in ('', 'comments', 'links', 'components', 'resources',)) and waffle.flag_is_active(request, features.EMBER_REGISTRIES_DETAIL_PAGE): + elif isinstance(resource, Registration) and (clean_suffix in ('', 'comments', 'links', 'components', 'resources',)) and flag_is_active(request, features.EMBER_REGISTRIES_DETAIL_PAGE): return use_ember_app() - elif isinstance(resource, Registration) and clean_suffix and clean_suffix.startswith('metadata') and waffle.flag_is_active(request, features.EMBER_REGISTRIES_DETAIL_PAGE): + elif isinstance(resource, Registration) and clean_suffix and clean_suffix.startswith('metadata') and flag_is_active(request, features.EMBER_REGISTRIES_DETAIL_PAGE): return use_ember_app() - elif isinstance(resource, Registration) and (clean_suffix in ('files', 'files/osfstorage')) and waffle.flag_is_active(request, features.EMBER_REGISTRATION_FILES): + elif isinstance(resource, Registration) and (clean_suffix in ('files', 'files/osfstorage')) and flag_is_active(request, features.EMBER_REGISTRATION_FILES): return use_ember_app() - elif isinstance(resource, Node) and clean_suffix and any(path.startswith(clean_suffix) for path in addon_paths) and waffle.flag_is_active(request, features.EMBER_PROJECT_FILES): + elif isinstance(resource, Node) and clean_suffix and any(path.startswith(clean_suffix) for path in addon_paths) and flag_is_active(request, features.EMBER_PROJECT_FILES): return use_ember_app() elif isinstance(resource, Node) and clean_suffix and clean_suffix.startswith('metadata'): return use_ember_app() elif isinstance(resource, BaseFileNode) and resource.is_file and not isinstance(resource.target, Preprint): - if isinstance(resource.target, Registration) and waffle.flag_is_active(request, features.EMBER_FILE_REGISTRATION_DETAIL): + if isinstance(resource.target, Registration) and flag_is_active(request, features.EMBER_FILE_REGISTRATION_DETAIL): return use_ember_app() - if isinstance(resource.target, Node) and waffle.flag_is_active(request, features.EMBER_FILE_PROJECT_DETAIL): + if isinstance(resource.target, Node) and flag_is_active(request, features.EMBER_FILE_PROJECT_DETAIL): return use_ember_app() # Redirect to legacy endpoint for Nodes, Wikis etc. @@ -390,7 +391,7 @@ def redirect_to_cos_news(**kwargs): def redirect_to_registration_workflow(**kwargs): # Redirect to making new registration - return redirect(furl(DOMAIN).add(path='registries/osf/new').url) + return redirect(furl.furl(DOMAIN).add(path='registries/osf/new').url) # Return error for legacy SHARE v1 search route @@ -398,7 +399,7 @@ def legacy_share_v1_search(**kwargs): return HTTPError( http_status.HTTP_400_BAD_REQUEST, data=dict( - message_long=f'Please use v2 of the SHARE search API available at {settings.SHARE_URL}api/v2/share/search/creativeworks/_search.' + message_long='Please use v2 of the SHARE search API available at {}api/v2/share/search/creativeworks/_search.'.format(settings.SHARE_URL) ) )