def test_is_gate_active(): request = factory.get('/') assert not is_gate_active(request, 'foo') request.version = 'v2' assert is_gate_active(request, 'foo')
def to_representation(self, instance): data = super().to_representation(instance) request = self.context.get('request', None) if request and is_gate_active(request, 'del-version-license-is-custom'): data.pop('is_custom', None) if request and is_gate_active(request, 'del-version-license-slug'): data.pop('slug', None) return data
def to_representation(self, obj): data = super().to_representation(obj) request = self.context.get('request', None) if request and not is_gate_active(request, 'platform-shim'): data.pop('platform', None) if request and not is_gate_active(request, 'is-restart-required-shim'): data.pop('is_restart_required', None) if request and not is_gate_active(request, 'is-webextension-shim'): data.pop('is_webextension', None) return data
def to_representation(self, obj): data = super(AddonSerializer, self).to_representation(obj) request = self.context.get('request', None) if request and is_gate_active(request, 'del-addons-created-field'): data.pop('created', None) if request and not is_gate_active(request, 'is-source-public-shim'): data.pop('is_source_public', None) if request and not is_gate_active(request, 'is-featured-addon-shim'): data.pop('is_featured', None) return data
def test_is_gate_active_explicit_upgrades(): # Test that we're not implicitly upgrading feature gates request = factory.get('/') assert not is_gate_active(request, 'baa') request.version = 'v2' assert not is_gate_active(request, 'baa') request.version = 'v3' assert is_gate_active(request, 'baa')
def to_representation(self, obj): data = super(AddonSerializer, self).to_representation(obj) request = self.context.get('request', None) if ('request' in self.context and 'wrap_outgoing_links' in self.context['request'].GET): for key in ('homepage', 'support_url', 'contributions_url'): if key in data: data[key] = self.outgoingify(data[key]) if request and is_gate_active(request, 'del-addons-created-field'): data.pop('created', None) if request and not is_gate_active(request, 'is-source-public-shim'): data.pop('is_source_public', None) return data
def get_serializer_class(self): if (self.action == 'list' and self.request and not is_gate_active( self.request, 'keep-license-text-in-version-list')): serializer_class = VersionListSerializer else: serializer_class = VersionSerializer return serializer_class
def to_representation(self, instance): request = self.context.get('request') out = super( CollectionAddonSerializer, self).to_representation(instance) if request and is_gate_active(request, 'collections-downloads-shim'): out['downloads'] = 0 return out
def to_representation(self, instance): request = self.context.get('request') out = super(CollectionAddonSerializer, self).to_representation(instance) if request and is_gate_active(request, 'collections-downloads-shim'): out['downloads'] = 0 return out
def to_representation(self, obj): data = super(ESAddonSerializer, self).to_representation(obj) request = self.context.get('request') if request and '_score' in data and not is_gate_active( request, 'addons-search-_score-field'): data.pop('_score') return data
def to_representation(self, instance): data = super(LicenseSerializer, self).to_representation(instance) request = self.context.get('request', None) if request and is_gate_active( request, 'del-version-license-is-custom'): data.pop('is_custom', None) return data
def to_representation(self, instance): out = super(BaseRatingSerializer, self).to_representation(instance) if self.request and is_gate_active(self.request, 'ratings-title-shim'): out['title'] = None if not self.context['view'].should_include_flags(): out.pop('flags', None) return out
def to_representation(self, value): data = super().to_representation(value) request = self.context.get('request', None) if request and is_gate_active(request, 'wrap-outgoing-parameter'): if data and 'wrap_outgoing_links' in request.GET: if isinstance(data, str): return get_outgoing_url(data) elif isinstance(data, dict): return { key: get_outgoing_url(value) if value else None for key, value in data.items() } # None or empty string... don't bother. return data if not data: return None if isinstance(data, dict): outgoing = { key: value if key == '_default' else get_outgoing_url(value) if value else None for key, value in data.items() } else: outgoing = get_outgoing_url(str(data)) return {'url': data, 'outgoing': outgoing}
def __init__(self, *args, **kwargs): super(RatingSerializer, self).__init__(*args, **kwargs) request = kwargs.get('context', {}).get('request') if request and is_gate_active(request, 'ratings-rating-shim'): score_field = self.fields.pop('score') score_field.source = None # drf complains if we specifiy source. self.fields['rating'] = score_field
def to_representation(self, instance): out = super().to_representation(instance) if self.request and is_gate_active(self.request, 'ratings-title-shim'): out['title'] = None if not self.context['view'].should_include_flags(): out.pop('flags', None) return out
def to_representation(self, obj): data = super().to_representation(obj) request = self.context.get('request', None) if request and is_gate_active(request, 'del-accounts-fxa-edit-email-url'): data.pop('fxa_edit_email_url', None) return data
def to_representation(self, instance): repr = super().to_representation(instance) request = self.context.get('request', None) if 'file' in repr and request and is_gate_active(request, 'version-files'): # In v3/v4 files is expected to be a list but now we only have one file. repr['files'] = [repr.pop('file')] return repr
def filter_queryset(self, qs): if self.action == 'list': addon_identifier = self.request.GET.get('addon') user_identifier = self.request.GET.get('user') version_identifier = self.request.GET.get('version') score_filter = ( self.request.GET.get('score') if is_gate_active(self.request, 'ratings-score-filter') else None) exclude_ratings = self.request.GET.get('exclude_ratings') if addon_identifier: qs = qs.filter(addon=self.get_addon_object()) if user_identifier: try: user_identifier = int(user_identifier) except ValueError: raise ParseError('user parameter should be an integer.') qs = qs.filter(user=user_identifier) if version_identifier: try: version_identifier = int(version_identifier) except ValueError: raise ParseError('version parameter should be an integer.') qs = qs.filter(version=version_identifier) elif addon_identifier: # When filtering on addon but not on version, only return the # latest rating posted by each user. qs = qs.filter(is_latest=True) if not addon_identifier and not user_identifier: # Don't allow listing ratings without filtering by add-on or # user. raise ParseError('Need an addon or user parameter') if user_identifier and addon_identifier and version_identifier: # When user, addon and version identifiers are set, we are # effectively only looking for one or zero objects. Fake # pagination in that case, avoiding all count() calls and # therefore related cache-machine invalidation issues. Needed # because the frontend wants to call this before and after # having posted a new rating, and needs accurate results. self.pagination_class = OneOrZeroPageNumberPagination if score_filter: try: scores = [int(score) for score in score_filter.split(',')] except ValueError: raise ParseError( 'score parameter should be an integer or a list of ' 'integers (separated by a comma).') qs = qs.filter(rating__in=scores) if exclude_ratings: try: exclude_ratings = [ int(rating) for rating in exclude_ratings.split(',') ] except ValueError: raise ParseError('exclude_ratings parameter should be an ' 'integer or a list of integers ' '(separated by a comma).') qs = qs.exclude(pk__in=exclude_ratings) return super(RatingViewSet, self).filter_queryset(qs)
def to_representation(self, obj): data = super(UserProfileSerializer, self).to_representation(obj) request = self.context.get('request', None) if request and is_gate_active(request, 'del-accounts-fxa-edit-email-url'): data.pop('fxa_edit_email_url', None) return data
def __init__(self, *args, **kwargs): super(RatingSerializer, self).__init__(*args, **kwargs) score_to_rating = (self.request and is_gate_active( self.request, 'ratings-rating-shim')) if score_to_rating: score_field = self.fields.pop('score') score_field.source = None # drf complains if we specifiy source. self.fields['rating'] = score_field
def to_representation(self, instance): data = super().to_representation(instance) request = self.context.get('request', None) if request and not is_gate_active( request, 'disco-heading-and-description-shim'): data.pop('heading', None) data.pop('description', None) return data
def get_attribute(self, obj): # For l10n_flat_input_output, make sure to always return a string as before. attribute = super().get_attribute(obj) if attribute is None: request = self.context.get('request', None) if is_gate_active(request, 'l10n_flat_input_output'): attribute = '' return attribute
def __init__(self, *args, **kwargs): super(RatingSerializer, self).__init__(*args, **kwargs) score_to_rating = ( self.request and is_gate_active(self.request, 'ratings-rating-shim')) if score_to_rating: score_field = self.fields.pop('score') score_field.source = None # drf complains if we specify source. self.fields['rating'] = score_field
def get_requested_language(self): # For l10n_flat_input_output, if the request didn't specify a `lang=xx` then # fake it as `lang=en-US` so we get a single (flat) result. requested = super().get_requested_language() if not requested: request = self.context.get('request', None) if is_gate_active(request, 'l10n_flat_input_output'): requested = settings.LANGUAGE_CODE return requested
def filter_queryset(self, qs): if self.action == 'list': addon_identifier = self.request.GET.get('addon') user_identifier = self.request.GET.get('user') version_identifier = self.request.GET.get('version') score_filter = (self.request.GET.get('score') if is_gate_active( self.request, 'ratings-score-filter') else None) exclude_ratings = self.request.GET.get('exclude_ratings') if addon_identifier: qs = qs.filter(addon=self.get_addon_object()) if user_identifier: try: user_identifier = int(user_identifier) except ValueError: raise ParseError('user parameter should be an integer.') qs = qs.filter(user=user_identifier) if version_identifier: try: version_identifier = int(version_identifier) except ValueError: raise ParseError('version parameter should be an integer.') qs = qs.filter(version=version_identifier) elif addon_identifier: # When filtering on addon but not on version, only return the # latest rating posted by each user. qs = qs.filter(is_latest=True) if not addon_identifier and not user_identifier: # Don't allow listing ratings without filtering by add-on or # user. raise ParseError('Need an addon or user parameter') if user_identifier and addon_identifier and version_identifier: # When user, addon and version identifiers are set, we are # effectively only looking for one or zero objects. Fake # pagination in that case, avoiding all count() calls and # therefore related cache-machine invalidation issues. Needed # because the frontend wants to call this before and after # having posted a new rating, and needs accurate results. self.pagination_class = OneOrZeroPageNumberPagination if score_filter: try: scores = [int(score) for score in score_filter.split(',')] except ValueError: raise ParseError( 'score parameter should be an integer or a list of ' 'integers (separated by a comma).') qs = qs.filter(rating__in=scores) if exclude_ratings: try: exclude_ratings = [ int(rating) for rating in exclude_ratings.split(',') ] except ValueError: raise ParseError('exclude_ratings parameter should be an ' 'integer or a list of integers ' '(separated by a comma).') qs = qs.exclude(pk__in=exclude_ratings) return super(RatingViewSet, self).filter_queryset(qs)
def get_requested_language(self): # For l10n_flat_input_output, if the request didn't specify a `lang=xx` then # fake it with the current locale so we get a single (flat) result. requested = super().get_requested_language() if not requested: request = self.context.get('request', None) if is_gate_active(request, 'l10n_flat_input_output'): requested = get_language() return requested
def to_representation(self, obj): data = super(LanguageToolsSerializer, self).to_representation(obj) request = self.context['request'] if (AddonAppVersionQueryParam.query_param not in request.GET and 'current_compatible_version' in data): data.pop('current_compatible_version') if request and is_gate_active( request, 'addons-locale_disambiguation-shim'): data['locale_disambiguation'] = None return data
def to_representation(self, obj): data = super(LanguageToolsSerializer, self).to_representation(obj) request = self.context['request'] if (AddonAppVersionQueryParam.query_param not in request.GET and 'current_compatible_version' in data): data.pop('current_compatible_version') if request and is_gate_active(request, 'addons-locale_disambiguation-shim'): data['locale_disambiguation'] = None return data
def get_user(self, obj): """Return minimal user information from ActivityLog. id, username and url are present for backwards-compatibility in v3 API only.""" data = { 'name': obj.user.name, } request = self.context.get('request') if request and is_gate_active(request, 'activity-user-shim'): data.update({'id': None, 'username': None, 'url': None}) return data
def to_representation(self, obj): data = super().to_representation(obj) request = self.context.get('request', None) # when 'wrap-outgoing-parameter' is on url is a flat string already is_flat_url = request and is_gate_active(request, 'wrap-outgoing-parameter') url = data.get('url') if not url: if obj.endpoint == Shelf.Endpoints.SEARCH: query_string = '&'.join( f'{key}={value}' for key, value in obj.get_param_dict().items()) fallback = absolutify( f'{reverse("search.search")}?{query_string}') elif obj.endpoint == Shelf.Endpoints.RANDOM_TAG: tag_page_url = reverse('tags.detail', kwargs={'tag_name': obj.tag}) query_string = '&'.join( f'{key}={value}' for key, value in obj.get_param_dict().items() if key != 'tag') fallback = absolutify(f'{tag_page_url}?{query_string}') elif obj.endpoint == Shelf.Endpoints.COLLECTIONS: fallback = absolutify( reverse( 'collections.detail', kwargs={ 'user_id': str(settings.TASK_USER_ID), 'slug': obj.criteria, }, )) else: # shouldn't happen fallback = None url = ({ 'url': fallback, 'outgoing': fallback } if not is_flat_url else fallback) # text = data.get('text') if is_flat_url: return { **data, 'url': url, } else: return { **data, 'url': (url or {}).get('url'), 'outgoing': (url or {}).get('outgoing'), }
def get_queryset(self): """Return the right base queryset depending on the situation.""" requested = self.request.GET.get('filter') valid_filters = ( 'all_with_deleted', 'all_with_unlisted', 'all_without_unlisted', ) if requested is not None: if self.action != 'list': raise serializers.ValidationError( 'The "filter" parameter is not valid in this context.' ) elif requested not in valid_filters: raise serializers.ValidationError( 'Invalid "filter" parameter specified.' ) # When listing, by default we only want to return listed, approved # versions, matching frontend needs - this can be overridden by the # filter in use. When fetching a single instance however, we use the # same queryset as the less restrictive filter, that allows us to see # all versions, even deleted ones. In any case permission checks will # prevent access if necessary (meaning regular users will never see # deleted or unlisted versions regardless of the queryset being used). # We start with the <Addon>.versions manager in order to have the # `addon` property preloaded on each version we return. addon = self.get_addon_object() if requested == 'all_with_deleted' or self.action != 'list': queryset = addon.versions(manager='unfiltered_for_relations').all() elif requested == 'all_with_unlisted': queryset = addon.versions.all() elif requested == 'all_without_unlisted': queryset = addon.versions.filter(channel=amo.RELEASE_CHANNEL_LISTED) else: queryset = addon.versions.filter( file__status=amo.STATUS_APPROVED, channel=amo.RELEASE_CHANNEL_LISTED ) # FIXME: we want to prefetch file.webext_permission instances in here if ( self.action == 'list' and self.request and not is_gate_active(self.request, 'keep-license-text-in-version-list') ): # When listing, we use the transformer_license (it's not active by default) # to fetch licenses & their translations in one (two with translations) # query regardless of the number of versions. # Note that this transformer defers text, which we don't need when listing. # This means we can't use it in API versions where the # 'keep-license-text-in-version-list' gate is active, so that endpoint # doesn't scale as nicely in those versions. queryset = queryset.transform(Version.transformer_license) return queryset
def get_user(self, obj): """Return minimal user information using ActivityLog.author_name to avoid revealing actual name of reviewers for their review actions if they have set an alias. id, username and url are present for backwards-compatibility in v3 API only.""" data = { 'name': obj.author_name, } request = self.context.get('request') if request and is_gate_active(request, 'activity-user-shim'): data.update({'id': None, 'username': None, 'url': None}) return data
def to_representation(self, instance): data = super().to_representation(instance) request = self.context.get('request', None) if request and not is_gate_active( request, 'disco-heading-and-description-shim'): data.pop('heading', None) data.pop('description', None) # if there wasn't a custom description, swap it out for the addon summary addon_summary = data.pop('addon_summary', None) if (not data.get('description_text') and instance.should_fallback_to_addon_summary and addon_summary): data['description_text'] = addon_summary return data
def get_serializer_class(self): use_developer_serializer = getattr( self.request, 'user', None) and acl.author_or_unlisted_viewer_or_reviewer( self.request, self.get_addon_object()) if (self.action == 'list' and self.request and not is_gate_active( self.request, 'keep-license-text-in-version-list')): serializer_class = (ListVersionSerializer if not use_developer_serializer else DeveloperListVersionSerializer) else: serializer_class = (VersionSerializer if not use_developer_serializer else DeveloperVersionSerializer) return serializer_class
def to_representation(self, obj): data = super().to_representation(obj) if data.get('url') or data.get('text'): request = self.context.get('request', None) if request and is_gate_active(request, 'wrap-outgoing-parameter'): # when 'wrap-outgoing-parameter' is on url is a flat string already return data else: url = data.get('url') or {} return { **data, 'url': url.get('url'), 'outgoing': url.get('outgoing'), } else: return None
def get_user(self, obj): """Return minimal user information using ActivityLog.author_name to avoid revealing actual name of reviewers for their review actions if they have set an alias. id, username and url are present for backwards-compatibility in v3 API only.""" data = { 'name': obj.author_name, } request = self.context.get('request') if request and is_gate_active(request, 'activity-user-shim'): data.update({ 'id': None, 'username': None, 'url': None }) return data
def should_include_flags(self): if not hasattr(self, '_should_include_flags'): request = self.request self._should_include_flags = ( 'show_flags_for' in request.GET and not is_gate_active(request, 'del-ratings-flags')) if self._should_include_flags: # Check the parameter was sent correctly try: show_flags_for = serializers.IntegerField( ).to_internal_value(request.GET['show_flags_for']) if show_flags_for != request.user.pk: raise serializers.ValidationError except serializers.ValidationError: raise ParseError( 'show_flags_for parameter value should be equal to ' 'the user id of the authenticated user') return self._should_include_flags
def should_include_flags(self): if not hasattr(self, '_should_include_flags'): request = self.request self._should_include_flags = ( 'show_flags_for' in request.GET and not is_gate_active(request, 'del-ratings-flags') ) if self._should_include_flags: # Check the parameter was sent correctly try: show_flags_for = ( serializers.IntegerField().to_internal_value( request.GET['show_flags_for'])) if show_flags_for != request.user.pk: raise serializers.ValidationError except serializers.ValidationError: raise ParseError( 'show_flags_for parameter value should be equal to ' 'the user id of the authenticated user') return self._should_include_flags
def get_paginated_response(self, data): request = self.request extra_data = {} if 'show_grouped_ratings' in request.GET: try: show_grouped_ratings = serializers.BooleanField().to_internal_value( request.GET['show_grouped_ratings'] ) except serializers.ValidationError: raise ParseError('show_grouped_ratings parameter should be a boolean') if show_grouped_ratings and self.get_addon_object(): extra_data['grouped_ratings'] = dict( GroupedRating.get(self.addon_object.id) ) if 'show_permissions_for' in request.GET and is_gate_active( self.request, 'ratings-can_reply' ): if 'addon' not in request.GET: raise ParseError( 'show_permissions_for parameter is only valid if the ' 'addon parameter is also present' ) try: show_permissions_for = serializers.IntegerField().to_internal_value( request.GET['show_permissions_for'] ) if show_permissions_for != request.user.pk: raise serializers.ValidationError except serializers.ValidationError: raise ParseError( 'show_permissions_for parameter value should be equal to ' 'the user id of the authenticated user' ) extra_data['can_reply'] = self.check_can_reply_permission_for_ratings_list() # Call this here so the validation checks on the `show_flags_for` are # carried out even when there are no results to serialize. self.should_include_flags() response = super(RatingViewSet, self).get_paginated_response(data) if extra_data: response.data.update(extra_data) return response
def get_paginated_response(self, data): request = self.request extra_data = {} if 'show_grouped_ratings' in request.GET: try: show_grouped_ratings = ( serializers.BooleanField().to_internal_value( request.GET['show_grouped_ratings'])) except serializers.ValidationError: raise ParseError( 'show_grouped_ratings parameter should be a boolean') if show_grouped_ratings and self.get_addon_object(): extra_data['grouped_ratings'] = dict(GroupedRating.get( self.addon_object.id)) if ('show_permissions_for' in request.GET and is_gate_active(self.request, 'ratings-can_reply')): if 'addon' not in request.GET: raise ParseError( 'show_permissions_for parameter is only valid if the ' 'addon parameter is also present') try: show_permissions_for = ( serializers.IntegerField().to_internal_value( request.GET['show_permissions_for'])) if show_permissions_for != request.user.pk: raise serializers.ValidationError except serializers.ValidationError: raise ParseError( 'show_permissions_for parameter value should be equal to ' 'the user id of the authenticated user') extra_data['can_reply'] = ( self.check_can_reply_permission_for_ratings_list()) # Call this here so the validation checks on the `show_flags_for` are # carried out even when there are no results to serialize. self.should_include_flags() response = super(RatingViewSet, self).get_paginated_response(data) if extra_data: response.data.update(extra_data) return response
def to_representation(self, obj): data = super(AddonSerializer, self).to_representation(obj) request = self.context.get('request', None) if 'theme_data' in data and data['theme_data'] is None: data.pop('theme_data') if ('request' in self.context and 'wrap_outgoing_links' in self.context['request'].GET): for key in ('homepage', 'support_url', 'contributions_url'): if key in data: data[key] = self.outgoingify(data[key]) if obj.type == amo.ADDON_PERSONA: if 'weekly_downloads' in data: # weekly_downloads don't make sense for lightweight themes. data.pop('weekly_downloads') if ('average_daily_users' in data and not self.is_broken_persona(obj)): # In addition, their average_daily_users number must come from # the popularity field of the attached Persona. data['average_daily_users'] = obj.persona.popularity if request and is_gate_active(request, 'del-addons-created-field'): data.pop('created', None) return data
def to_representation(self, instance): out = super(BaseRatingSerializer, self).to_representation(instance) if self.request and is_gate_active(self.request, 'ratings-title-shim'): out['title'] = None return out
def flat(self): request = self.context.get('request', None) return is_gate_active(request, 'l10n_flat_input_output')