Expose latest unlisted version in API for authors/unlisted reviewers

This commit is contained in:
Mathieu Pillard 2016-11-07 13:51:32 +01:00
Родитель 2248a2d5ab
Коммит fe0cd88b38
13 изменённых файлов: 334 добавлений и 36 удалений

Просмотреть файл

@ -125,6 +125,7 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
:>json boolean is_source_public: Whether the add-on source is publicly viewable or not.
:>json string|object|null name: The add-on name (See :ref:`translated fields <api-overview-translations>`).
:>json string last_updated: The date of the last time the add-on was updated by its developer(s).
:>json object|null latest_unlisted_version: Object holding the latest unlisted :ref:`version <version-detail-object>` of the add-on. This field is only present if the user has unlisted reviewer permissions, or is listed as a developer of the add-on.
:>json array previews: Array holding information about the previews for the add-on.
:>json int previews[].id: The id for a preview.
:>json string|object|null previews[].caption: The caption describing a preview (See :ref:`translated fields <api-overview-translations>`).

Просмотреть файл

@ -78,6 +78,7 @@ class AddonIndexer(BaseSearchIndexer):
'is_experimental': {'type': 'boolean'},
'is_listed': {'type': 'boolean'},
'last_updated': {'type': 'date'},
'latest_unlisted_version': version_mapping,
'listed_authors': {
'type': 'object',
'properties': {
@ -160,7 +161,7 @@ class AddonIndexer(BaseSearchIndexer):
} for file_ in version_obj.all_files],
'reviewed': version_obj.reviewed,
'version': version_obj.version,
}
} if version_obj else None
@classmethod
def extract_compatibility_info(cls, version_obj):
@ -234,11 +235,8 @@ class AddonIndexer(BaseSearchIndexer):
obj.current_version.supported_platforms]
else:
data['has_version'] = None
if obj.current_beta_version:
data['current_beta_version'] = cls.extract_version(
obj, obj.current_beta_version)
else:
data['current_beta_version'] = None
data['current_beta_version'] = cls.extract_version(
obj, obj.current_beta_version)
data['listed_authors'] = [
{'name': a.name, 'id': a.id, 'username': a.username}
for a in obj.listed_authors
@ -247,6 +245,9 @@ class AddonIndexer(BaseSearchIndexer):
data['has_eula'] = bool(obj.eula)
data['has_privacy_policy'] = bool(obj.privacy_policy)
data['latest_unlisted_version'] = cls.extract_version(
obj, obj.latest_unlisted_version)
# We can use all_previews because the indexing code goes through the
# transformer that sets it.
data['previews'] = [{'id': preview.id, 'modified': preview.modified}

Просмотреть файл

@ -954,6 +954,12 @@ class Addon(OnChangeMixin, ModelBase):
pass
return None
@amo.cached_property(writable=True)
def latest_unlisted_version(self):
"""Shortcut property for Addon.find_latest_version(
channel=RELEASE_CHANNEL_UNLISTED)."""
return self.find_latest_version(channel=amo.RELEASE_CHANNEL_UNLISTED)
@amo.cached_property
def binary(self):
"""Returns if the current version has binary files."""

Просмотреть файл

@ -179,8 +179,8 @@ class AddonSerializer(serializers.ModelSerializer):
'is_experimental',
'is_listed',
'is_source_public',
'name',
'last_updated',
'name',
'previews',
'public_stats',
'ratings',
@ -275,9 +275,15 @@ class AddonSerializer(serializers.ModelSerializer):
return False
class ESAddonSerializer(BaseESSerializer, AddonSerializer):
previews = ESPreviewSerializer(many=True, source='all_previews')
class AddonSerializerWithUnlistedData(AddonSerializer):
latest_unlisted_version = SimpleVersionSerializer()
class Meta:
model = Addon
fields = AddonSerializer.Meta.fields + ('latest_unlisted_version',)
class ESBaseAddonSerializer(BaseESSerializer):
datetime_fields = ('created', 'last_updated', 'modified')
translated_fields = ('name', 'description', 'homepage', 'summary',
'support_email', 'support_url')
@ -351,12 +357,15 @@ class ESAddonSerializer(BaseESSerializer, AddonSerializer):
# Attach related models (also faking them). `current_version` is a
# property we can't write to, so we use the underlying field which
# begins with an underscore. `current_beta_version` is a
# cached_property so we can directly write to it.
# begins with an underscore. `current_beta_version` and
# `latest_unlisted_version` are writeable cached_property so we can
# directly write to them.
obj.current_beta_version = self.fake_version_object(
obj, data.get('current_beta_version'))
obj._current_version = self.fake_version_object(
obj, data.get('current_version'))
obj.latest_unlisted_version = self.fake_version_object(
obj, data.get('latest_unlisted_version'))
data_authors = data.get('listed_authors', [])
obj.listed_authors = [
@ -398,6 +407,15 @@ class ESAddonSerializer(BaseESSerializer, AddonSerializer):
return obj
class ESAddonSerializer(ESBaseAddonSerializer, AddonSerializer):
previews = ESPreviewSerializer(many=True, source='all_previews')
class ESAddonSerializerWithUnlistedData(
ESBaseAddonSerializer, AddonSerializerWithUnlistedData):
previews = ESPreviewSerializer(many=True, source='all_previews')
class StaticCategorySerializer(serializers.Serializer):
"""Serializes a `StaticCategory` as found in constants.categories"""
id = serializers.IntegerField()

Просмотреть файл

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from datetime import timedelta
from itertools import chain
from olympia import amo
@ -47,9 +48,9 @@ class TestAddonIndexer(TestCase):
complex_fields = [
'app', 'boost', 'category', 'current_beta_version',
'current_version', 'description', 'has_eula', 'has_privacy_policy',
'has_theme_rereview', 'has_version', 'listed_authors', 'name',
'name_sort', 'platforms', 'previews', 'public_stats', 'ratings',
'summary', 'tags',
'has_theme_rereview', 'has_version', 'latest_unlisted_version',
'listed_authors', 'name', 'name_sort', 'platforms', 'previews',
'public_stats', 'ratings', 'summary', 'tags',
]
# Fields that need to be present in the mapping, but might be skipped
@ -142,6 +143,7 @@ class TestAddonIndexer(TestCase):
assert extracted['current_beta_version'] is None
assert extracted['current_version']
assert extracted['has_theme_rereview'] is None
assert extracted['latest_unlisted_version'] is None
assert extracted['listed_authors'] == [
{'name': u'55021 التطب', 'id': 55021, 'username': '55021'}]
assert extracted['platforms'] == [PLATFORM_ALL.id]
@ -165,10 +167,16 @@ class TestAddonIndexer(TestCase):
assert extracted['has_privacy_policy'] is False
def test_extract_version_and_files(self):
current_beta_version = version_factory(
addon=self.addon, file_kw={'status': amo.STATUS_BETA})
version = self.addon.current_version
file_factory(version=version, platform=PLATFORM_MAC.id)
current_beta_version = version_factory(
addon=self.addon, file_kw={'status': amo.STATUS_BETA})
unlisted_version = version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
# FIXME: remove this next line once current_version is modified to only
# return listed versions.
unlisted_version.update(
created=version.created - timedelta(days=42))
extracted = self._extract()
assert extracted['current_version']
@ -218,6 +226,29 @@ class TestAddonIndexer(TestCase):
assert extracted_file['size'] == file_.size
assert extracted_file['status'] == file_.status
version = unlisted_version
assert extracted['latest_unlisted_version']
assert extracted['latest_unlisted_version']['id'] == version.pk
assert extracted['latest_unlisted_version']['compatible_apps'] == {
FIREFOX.id: {
'min': 4009900200100L,
'max': 5009900200100L,
'max_human': '5.0.99',
'min_human': '4.0.99',
}
}
assert (
extracted['latest_unlisted_version']['version'] == version.version)
for idx, file_ in enumerate(version.all_files):
extracted_file = extracted['latest_unlisted_version']['files'][idx]
assert extracted_file['id'] == file_.pk
assert extracted_file['created'] == file_.created
assert extracted_file['filename'] == file_.filename
assert extracted_file['hash'] == file_.hash
assert extracted_file['platform'] == file_.platform
assert extracted_file['size'] == file_.size
assert extracted_file['status'] == file_.status
def test_extract_translations(self):
translations_name = {
'en-US': u'Name in ënglish',

Просмотреть файл

@ -425,6 +425,32 @@ class TestAddonModels(TestCase):
a = Addon.objects.get(pk=3723)
assert a.current_version is None
def test_latest_unlisted_version(self):
addon = Addon.objects.get(pk=3615)
an_unlisted_version = version_factory(
addon=addon, version='3.0', channel=amo.RELEASE_CHANNEL_UNLISTED)
an_unlisted_version.update(created=self.days_ago(2))
a_newer_unlisted_version = version_factory(
addon=addon, version='4.0', channel=amo.RELEASE_CHANNEL_UNLISTED)
a_newer_unlisted_version.update(created=self.days_ago(1))
version_factory(
addon=addon, version='5.0', channel=amo.RELEASE_CHANNEL_UNLISTED,
file_kw={'status': amo.STATUS_DISABLED})
assert addon.latest_unlisted_version == a_newer_unlisted_version
# Make sure the property is cached.
an_even_newer_unlisted_version = version_factory(
addon=addon, version='6.0', channel=amo.RELEASE_CHANNEL_UNLISTED)
assert addon.latest_unlisted_version == a_newer_unlisted_version
# Make sure it can be deleted to reset it.
del addon.latest_unlisted_version
assert addon.latest_unlisted_version == an_even_newer_unlisted_version
# Make sure it's writeable.
addon.latest_unlisted_version = an_unlisted_version
assert addon.latest_unlisted_version == an_unlisted_version
def test_find_latest_version(self):
"""
Tests that we get the latest version of an addon.

Просмотреть файл

@ -12,7 +12,8 @@ from olympia.addons.indexers import AddonIndexer
from olympia.addons.models import (
Addon, AddonCategory, AddonUser, Category, Persona, Preview)
from olympia.addons.serializers import (
AddonSerializer, ESAddonSerializer, VersionSerializer)
AddonSerializer, AddonSerializerWithUnlistedData, ESAddonSerializer,
ESAddonSerializerWithUnlistedData, VersionSerializer)
from olympia.addons.utils import generate_addon_guid
from olympia.constants.categories import CATEGORIES
from olympia.versions.models import ApplicationsVersions, AppVersion, License
@ -127,6 +128,9 @@ class AddonSerializerOutputTestMixin(object):
assert result['current_beta_version'] is None
# In this serializer latest_unlisted_version is omitted.
assert 'latest_unlisted_version' not in result
assert result['current_version']
self._test_version(
self.addon.current_version, result['current_version'])
@ -154,9 +158,9 @@ class AddonSerializerOutputTestMixin(object):
assert result['is_experimental'] == self.addon.is_experimental is False
assert result['is_listed'] == self.addon.is_listed
assert result['is_source_public'] == self.addon.view_source
assert result['name'] == {'en-US': self.addon.name}
assert result['last_updated'] == (
self.addon.last_updated.isoformat() + 'Z')
assert result['name'] == {'en-US': self.addon.name}
assert result['previews']
assert len(result['previews']) == 2
@ -200,6 +204,34 @@ class AddonSerializerOutputTestMixin(object):
return result
def test_latest_unlisted_version(self):
self.addon = addon_factory()
version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED,
version='1.1')
assert self.addon.latest_unlisted_version
result = self.serialize()
# In this serializer latest_unlisted_version is omitted even if there
# is one, because it's limited to users with specific rights.
assert 'latest_unlisted_version' not in result
def test_latest_unlisted_version_with_rights(self):
self.serializer_class = self.serializer_class_with_unlisted_data
self.addon = addon_factory()
version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED,
version='1.1')
assert self.addon.latest_unlisted_version
result = self.serialize()
# In this serializer latest_unlisted_version is present.
assert result['latest_unlisted_version']
self._test_version(
self.addon.latest_unlisted_version,
result['latest_unlisted_version'])
def test_current_beta_version(self):
self.addon = addon_factory()
@ -360,14 +392,21 @@ class AddonSerializerOutputTestMixin(object):
class TestAddonSerializerOutput(AddonSerializerOutputTestMixin, TestCase):
serializer_class = AddonSerializer
serializer_class_with_unlisted_data = AddonSerializerWithUnlistedData
def serialize(self):
self.serializer = self.serializer_class(
context={'request': self.request})
# Manually reload the add-on first to clear any cached properties.
self.addon = Addon.unfiltered.get(pk=self.addon.pk)
serializer = AddonSerializer(context={'request': self.request})
return serializer.to_representation(self.addon)
return self.serializer.to_representation(self.addon)
class TestESAddonSerializerOutput(AddonSerializerOutputTestMixin, ESTestCase):
serializer_class = ESAddonSerializer
serializer_class_with_unlisted_data = ESAddonSerializerWithUnlistedData
def tearDown(self):
super(TestESAddonSerializerOutput, self).tearDown()
self.empty_index('default')
@ -382,11 +421,13 @@ class TestESAddonSerializerOutput(AddonSerializerOutputTestMixin, ESTestCase):
return qs.filter('term', id=self.addon.pk).execute()[0]
def serialize(self):
self.serializer = self.serializer_class(
context={'request': self.request})
obj = self.search()
with self.assertNumQueries(0):
serializer = ESAddonSerializer(context={'request': self.request})
result = serializer.to_representation(obj)
result = self.serializer.to_representation(obj)
return result

Просмотреть файл

@ -1750,10 +1750,53 @@ class TestAddonViewSetDetail(AddonAndVersionViewSetDetailMixin, TestCase):
assert result['slug'] == 'my-addon'
assert result['last_updated'] == (
self.addon.last_updated.isoformat() + 'Z')
return result
def _set_tested_url(self, param):
self.url = reverse('addon-detail', kwargs={'pk': param})
def test_hide_latest_unlisted_version_anonymous(self):
unlisted_version = version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
unlisted_version.update(created=self.days_ago(1))
result = self._test_url()
assert 'latest_unlisted_version' not in result
def test_hide_latest_unlisted_version_simple_reviewer(self):
user = UserProfile.objects.create(username='reviewer')
self.grant_permission(user, 'Addons:Review')
self.client.login_api(user)
unlisted_version = version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
unlisted_version.update(created=self.days_ago(1))
result = self._test_url()
assert 'latest_unlisted_version' not in result
def test_show_latest_unlisted_version_author(self):
user = UserProfile.objects.create(username='author')
AddonUser.objects.create(user=user, addon=self.addon)
self.client.login_api(user)
unlisted_version = version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
unlisted_version.update(created=self.days_ago(1))
result = self._test_url()
assert result['latest_unlisted_version']
assert result['latest_unlisted_version']['id'] == unlisted_version.pk
def test_show_latest_unlisted_version_unlisted_reviewer(self):
user = UserProfile.objects.create(username='author')
self.grant_permission(user, 'Addons:ReviewUnlisted')
self.client.login_api(user)
unlisted_version = version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
unlisted_version.update(created=self.days_ago(1))
result = self._test_url()
assert result['latest_unlisted_version']
assert result['latest_unlisted_version']['id'] == unlisted_version.pk
class TestVersionViewSetDetail(AddonAndVersionViewSetDetailMixin, TestCase):
client_class = APITestClient
@ -2094,11 +2137,17 @@ class TestAddonSearchView(ESTestCase):
assert result['slug'] == 'my-addon'
assert result['last_updated'] == addon.last_updated.isoformat() + 'Z'
# latest_unlisted_version should never be exposed in public search.
assert 'latest_unlisted_version' not in result
result = data['results'][1]
assert result['id'] == addon2.pk
assert result['name'] == {'en-US': u'My second Addôn'}
assert result['slug'] == 'my-second-addon'
# latest_unlisted_version should never be exposed in public search.
assert 'latest_unlisted_version' not in result
def test_empty(self):
data = self.perform_search(self.url)
assert data['count'] == 0

Просмотреть файл

@ -33,6 +33,7 @@ from rest_framework.viewsets import GenericViewSet
from session_csrf import anonymous_csrf_exempt
from olympia import amo
from olympia.access import acl
from olympia.amo import messages
from olympia.amo.decorators import post_required
from olympia.amo.forms import AbuseForm
@ -65,8 +66,8 @@ from .indexers import AddonIndexer
from .models import Addon, Persona, FrozenAddon
from .serializers import (
AddonEulaPolicySerializer, AddonFeatureCompatibilitySerializer,
AddonSerializer, ESAddonSerializer, VersionSerializer,
StaticCategorySerializer)
AddonSerializer, AddonSerializerWithUnlistedData, ESAddonSerializer,
VersionSerializer, StaticCategorySerializer)
from .utils import get_creatured_ids, get_featured_ids
@ -615,13 +616,14 @@ class AddonViewSet(RetrieveModelMixin, GenericViewSet):
AllowReviewer, AllowReviewerUnlisted),
]
serializer_class = AddonSerializer
serializer_class_with_unlisted_data = AddonSerializerWithUnlistedData
addon_id_pattern = re.compile(
# Match {uuid} or something@host.tld ("something" being optional)
# guids. Copied from mozilla-central XPIProvider.jsm.
r'^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}'
r'|[a-z0-9-\._]*\@[a-z0-9-\._]+)$', re.IGNORECASE)
# Permission classes disallow access to non-public/unlisted add-ons unless
# logged in as a reviewer/addon owner/admin, so the unfiltered queryset
# logged in as a reviewer/addon owner/admin, so the with_unlisted queryset
# is fine here.
queryset = Addon.with_unlisted.all()
lookup_value_regex = '[^/]+' # Allow '.' for email-like guids.
@ -634,6 +636,17 @@ class AddonViewSet(RetrieveModelMixin, GenericViewSet):
return Addon.unfiltered.all()
return super(AddonViewSet, self).get_queryset()
def get_serializer_class(self):
# Override serializer to use serializer_class_with_unlisted_data if
# we are allowed to access unlisted data.
obj = getattr(self, 'instance')
request = self.request
if (acl.check_unlisted_addons_reviewer(request) or
(obj and request.user.is_authenticated() and
obj.authors.filter(pk=request.user.pk).exists())):
return self.serializer_class_with_unlisted_data
return self.serializer_class
def get_lookup_field(self, identifier):
lookup_field = 'pk'
if identifier and not identifier.isdigit():
@ -650,7 +663,8 @@ class AddonViewSet(RetrieveModelMixin, GenericViewSet):
identifier = self.kwargs.get('pk')
self.lookup_field = self.get_lookup_field(identifier)
self.kwargs[self.lookup_field] = identifier
return super(AddonViewSet, self).get_object()
self.instance = super(AddonViewSet, self).get_object()
return self.instance
@detail_route()
def feature_compatibility(self, request, pk=None):

Просмотреть файл

@ -672,6 +672,9 @@ def addon_factory(
addon = Addon.objects.create(type=type_, **kwargs)
# Save 2.
if 'channel' not in version_kw and 'is_listed' in kw:
version_kw['channel'] = (amo.RELEASE_CHANNEL_LISTED if kw['is_listed']
else amo.RELEASE_CHANNEL_UNLISTED)
version = version_factory(file_kw, addon=addon, **version_kw)
addon.update_version()
addon.status = status

Просмотреть файл

@ -5,8 +5,9 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import override_settings
from olympia import amo
from olympia.amo.tests import version_factory
from olympia.accounts.tests.test_views import BaseAuthenticationView
from olympia.addons.tests.test_views import AddonAndVersionViewSetDetailMixin
from olympia.addons.utils import generate_addon_guid
from olympia.amo.tests import (
addon_factory, APITestClient, ESTestCase, TestCase)
@ -93,12 +94,16 @@ class TestInternalAddonSearchView(ESTestCase):
assert result['name'] == {'en-US': u'My Addôn'}
assert result['slug'] == 'my-addon'
assert result['last_updated'] == addon.last_updated.isoformat() + 'Z'
assert result['latest_unlisted_version']
assert (result['latest_unlisted_version']['id'] ==
addon.latest_unlisted_version.pk)
result = data['results'][1]
assert result['id'] == addon2.pk
assert result['name'] == {'en-US': u'My second Addôn'}
assert result['slug'] is None # Because it was deleted.
assert result['status'] == 'deleted'
assert result['latest_unlisted_version'] is None
def test_empty(self):
data = self.perform_search_with_senior_editor(self.url)
@ -168,14 +173,17 @@ class TestInternalAddonSearchView(ESTestCase):
assert result['name'] == {'en-US': u'By second Addôn'}
class TestAddonViewSetDetail(AddonAndVersionViewSetDetailMixin, TestCase):
class TestInternalAddonViewSetDetail(TestCase):
client_class = APITestClient
def setUp(self):
super(TestAddonViewSetDetail, self).setUp()
super(TestInternalAddonViewSetDetail, self).setUp()
self.addon = addon_factory(
guid=generate_addon_guid(), name=u'My Addôn', slug='my-addon')
self._set_tested_url(self.addon.pk)
user = UserProfile.objects.create(username='reviewer-admin-tools')
self.grant_permission(user, 'ReviewerAdminTools:View')
self.client.login_api(user)
def _test_url(self):
response = self.client.get(self.url)
@ -186,10 +194,93 @@ class TestAddonViewSetDetail(AddonAndVersionViewSetDetailMixin, TestCase):
assert result['slug'] == 'my-addon'
assert result['last_updated'] == (
self.addon.last_updated.isoformat() + 'Z')
assert 'latest_unlisted_version' in result
return result
def _set_tested_url(self, param):
self.url = reverse('internal-addon-detail', kwargs={'pk': param})
def test_get_by_id(self):
self._test_url()
def test_get_by_slug(self):
self._set_tested_url(self.addon.slug)
self._test_url()
def test_get_by_guid(self):
self._set_tested_url(self.addon.guid)
self._test_url()
def test_get_by_guid_uppercase(self):
self._set_tested_url(self.addon.guid.upper())
self._test_url()
def test_get_by_guid_email_format(self):
self.addon.update(guid='my-addon@example.tld')
self._set_tested_url(self.addon.guid)
self._test_url()
def test_get_by_guid_email_short_format(self):
self.addon.update(guid='@example.tld')
self._set_tested_url(self.addon.guid)
self._test_url()
def test_get_by_guid_email_really_short_format(self):
self.addon.update(guid='@example')
self._set_tested_url(self.addon.guid)
self._test_url()
def test_get_anonymous(self):
self.client.logout_api()
response = self.client.get(self.url)
assert response.status_code == 401
def test_get_no_rights(self):
self.client.logout_api()
user = UserProfile.objects.create(username='simpleuser')
self.client.login_api(user)
response = self.client.get(self.url)
assert response.status_code == 403
def test_get_no_rights_even_if_reviewer(self):
self.client.logout_api()
user = UserProfile.objects.create(username='reviewer')
self.grant_permission(user, 'Addons:Review')
self.client.login_api(user)
response = self.client.get(self.url)
assert response.status_code == 403
def test_get_no_rights_even_if_author(self):
self.client.logout_api()
user = UserProfile.objects.create(username='author')
self.addon.addonuser_set.create(user=user, addon=self.addon)
self.client.login_api(user)
response = self.client.get(self.url)
assert response.status_code == 403
def test_get_not_listed(self):
self.addon.update(is_listed=False)
response = self.client.get(self.url)
assert response.status_code == 200
def test_get_deleted(self):
self.addon.delete()
response = self.client.get(self.url)
assert response.status_code == 200
def test_get_addon_not_found(self):
self._set_tested_url(self.addon.pk + 42)
response = self.client.get(self.url)
assert response.status_code == 404
def test_show_latest_unlisted_version_unlisted(self):
unlisted_version = version_factory(
addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED)
unlisted_version.update(created=self.days_ago(1))
result = self._test_url()
assert result['latest_unlisted_version']
assert result['latest_unlisted_version']['id'] == unlisted_version.pk
class TestLoginStartView(TestCase):

Просмотреть файл

@ -2,12 +2,11 @@ from django.conf.urls import include, patterns, url
from rest_framework.routers import SimpleRouter
from olympia.addons.views import AddonViewSet
from . import views
addons = SimpleRouter()
addons.register(r'addon', AddonViewSet, base_name='internal-addon')
addons.register(r'addon', views.InternalAddonViewSet,
base_name='internal-addon')
urlpatterns = patterns(

Просмотреть файл

@ -3,7 +3,10 @@ import logging
from django.conf import settings
from olympia.accounts.views import LoginBaseView, LoginStartBaseView
from olympia.addons.views import AddonSearchView
from olympia.addons.models import Addon
from olympia.addons.views import AddonViewSet, AddonSearchView
from olympia.addons.serializers import (
AddonSerializerWithUnlistedData, ESAddonSerializerWithUnlistedData)
from olympia.api.authentication import JSONWebTokenAuthentication
from olympia.api.permissions import AnyOf, GroupPermission
from olympia.search.filters import (
@ -16,9 +19,10 @@ class InternalAddonSearchView(AddonSearchView):
# AddonSearchView disables auth classes so we need to add it back.
authentication_classes = [JSONWebTokenAuthentication]
# Similar to AddonSearchView but without the PublicContentFilter and with
# InternalSearchParameterFilter instead of SearchParameterFilter to allow
# searching by status.
# Similar to AddonSearchView but without the ReviewedContentFilter (
# allowing unlisted, deleted, unreviewed addons to show up) and with
# InternalSearchParameterFilter instead of SearchParameterFilter (allowing
# to search by status).
filter_backends = [
SearchQueryFilter, InternalSearchParameterFilter, SortingFilter
]
@ -26,6 +30,20 @@ class InternalAddonSearchView(AddonSearchView):
# Restricted to specific permissions.
permission_classes = [AnyOf(GroupPermission('AdminTools', 'View'),
GroupPermission('ReviewerAdminTools', 'View'))]
# Can display unlisted data.
serializer_class = ESAddonSerializerWithUnlistedData
class InternalAddonViewSet(AddonViewSet):
# Restricted to specific permissions.
permission_classes = [AnyOf(GroupPermission('AdminTools', 'View'),
GroupPermission('ReviewerAdminTools', 'View'))]
# Internal tools allow access to everything, including deleted add-ons.
queryset = Addon.unfiltered.all()
# Can display unlisted data.
serializer_class = AddonSerializerWithUnlistedData
class LoginStartView(LoginStartBaseView):