Add support for beta versions in the add-on APIs
- Expose current_beta_version in detail/search responses - Allow filtering add-on versions list to show only betas
This commit is contained in:
Родитель
c5d7b03ba4
Коммит
b4ac83efcb
|
@ -103,6 +103,7 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
|
|||
:>json object compatibility: Object detailing the add-on :ref:`add-on application <addon-detail-application>` and version compatibility.
|
||||
:>json object compatibility[app_name].max: Maximum version of the corresponding app the add-on is compatible with.
|
||||
:>json object compatibility[app_name].min: Minimum version of the corresponding app the add-on is compatible with.
|
||||
:>json object current_beta_version: Object holding the current beta :ref:`version <version-detail-object>` of the add-on, if it exists. For performance reasons the ``license`` and ``release_notes`` fields are omitted.
|
||||
:>json object current_version: Object holding the current :ref:`version <version-detail-object>` of the add-on. For performance reasons the ``license`` and ``release_notes`` fields are omitted.
|
||||
:>json string default_locale: The add-on default locale for translations.
|
||||
:>json string|object|null description: The add-on description (See :ref:`translated fields <api-overview-translations>`).
|
||||
|
@ -237,7 +238,7 @@ This endpoint allows you to list all versions belonging to a specific add-on.
|
|||
By default, the version list API will only return versions with valid statuses
|
||||
(excluding versions that have incomplete, disabled, deleted, rejected or
|
||||
flagged for further review files) - you can change that with the ``filter``
|
||||
query parameter, which requires authentication and specific permissions
|
||||
query parameter, which may require authentication and specific permissions
|
||||
depending on the value:
|
||||
|
||||
================ ========================================================
|
||||
|
@ -248,6 +249,7 @@ This endpoint allows you to list all versions belonging to a specific add-on.
|
|||
a developer of the add-on.
|
||||
all_with_deleted Show all versions attached to this add-on, including
|
||||
deleted ones. Requires admin permissions.
|
||||
beta_only Show beta versions only.
|
||||
================ ========================================================
|
||||
|
||||
--------------
|
||||
|
|
|
@ -20,7 +20,7 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
@classmethod
|
||||
def get_mapping(cls):
|
||||
doc_name = cls.get_doctype_name()
|
||||
appver = {
|
||||
appver_mapping = {
|
||||
'properties': {
|
||||
'max': {'type': 'long'},
|
||||
'min': {'type': 'long'},
|
||||
|
@ -28,40 +28,47 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
'min_human': {'type': 'string', 'index': 'no'},
|
||||
}
|
||||
}
|
||||
version_mapping = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'compatible_apps': {'properties': {app.id: appver_mapping
|
||||
for app in amo.APP_USAGE}},
|
||||
'id': {'type': 'long', 'index': 'no'},
|
||||
'reviewed': {'type': 'date', 'index': 'no'},
|
||||
'files': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'long', 'index': 'no'},
|
||||
'created': {'type': 'date', 'index': 'no'},
|
||||
'hash': {'type': 'string', 'index': 'no'},
|
||||
'filename': {
|
||||
'type': 'string', 'index': 'no'},
|
||||
'platform': {
|
||||
'type': 'byte', 'index': 'no'},
|
||||
'size': {'type': 'long', 'index': 'no'},
|
||||
'status': {'type': 'byte'},
|
||||
}
|
||||
},
|
||||
'version': {'type': 'string', 'index': 'no'},
|
||||
}
|
||||
}
|
||||
mapping = {
|
||||
doc_name: {
|
||||
'properties': {
|
||||
'id': {'type': 'long'},
|
||||
|
||||
'app': {'type': 'byte'},
|
||||
'appversion': {'properties': {app.id: appver
|
||||
# FIXME: remove and update the code (search/views.py,
|
||||
# legacy_api/utils.py) to use the newest mapping that
|
||||
# replaces this field by current_version.compatible_apps.
|
||||
'appversion': {'properties': {app.id: appver_mapping
|
||||
for app in amo.APP_USAGE}},
|
||||
'average_daily_users': {'type': 'long'},
|
||||
'bayesian_rating': {'type': 'double'},
|
||||
'current_beta_version': version_mapping,
|
||||
'category': {'type': 'integer'},
|
||||
'created': {'type': 'date'},
|
||||
'current_version': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'long', 'index': 'no'},
|
||||
'reviewed': {'type': 'date', 'index': 'no'},
|
||||
'files': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'long', 'index': 'no'},
|
||||
'created': {'type': 'date', 'index': 'no'},
|
||||
'hash': {'type': 'string', 'index': 'no'},
|
||||
'filename': {
|
||||
'type': 'string', 'index': 'no'},
|
||||
'platform': {
|
||||
'type': 'byte', 'index': 'no'},
|
||||
'size': {'type': 'long', 'index': 'no'},
|
||||
'status': {'type': 'byte'},
|
||||
}
|
||||
},
|
||||
'version': {'type': 'string', 'index': 'no'},
|
||||
}
|
||||
},
|
||||
'current_version': version_mapping,
|
||||
'boost': {'type': 'float', 'null_value': 1.0},
|
||||
'default_locale': {'type': 'string', 'index': 'no'},
|
||||
'description': {'type': 'string', 'analyzer': 'snowball'},
|
||||
|
@ -142,6 +149,41 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
|
||||
return mapping
|
||||
|
||||
@classmethod
|
||||
def extract_version(cls, obj, version_obj):
|
||||
return {
|
||||
'id': version_obj.pk,
|
||||
'compatible_apps': cls.extract_compatibility_info(version_obj),
|
||||
'files': [{
|
||||
'id': file_.id,
|
||||
'created': file_.created,
|
||||
'filename': file_.filename,
|
||||
'hash': file_.hash,
|
||||
'platform': file_.platform,
|
||||
'size': file_.size,
|
||||
'status': file_.status,
|
||||
} for file_ in version_obj.all_files],
|
||||
'reviewed': version_obj.reviewed,
|
||||
'version': version_obj.version,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def extract_compatibility_info(cls, version_obj):
|
||||
compatible_apps = {}
|
||||
for app, appver in version_obj.compatible_apps.items():
|
||||
if appver:
|
||||
min_, max_ = appver.min.version_int, appver.max.version_int
|
||||
min_human, max_human = appver.min.version, appver.max.version
|
||||
else:
|
||||
# Fake wide compatibility for search tools and personas.
|
||||
min_, max_ = 0, version_int('9999')
|
||||
min_human, max_human = None, None
|
||||
compatible_apps[app.id] = {
|
||||
'min': min_, 'min_human': min_human,
|
||||
'max': max_, 'max_human': max_human,
|
||||
}
|
||||
return compatible_apps
|
||||
|
||||
@classmethod
|
||||
def extract_document(cls, obj):
|
||||
"""Extract indexable attributes from an add-on."""
|
||||
|
@ -182,19 +224,6 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
data['has_theme_rereview'] = None
|
||||
|
||||
data['app'] = [app.id for app in obj.compatible_apps.keys()]
|
||||
data['appversion'] = {}
|
||||
for app, appver in obj.compatible_apps.items():
|
||||
if appver:
|
||||
min_, max_ = appver.min.version_int, appver.max.version_int
|
||||
min_human, max_human = appver.min.version, appver.max.version
|
||||
else:
|
||||
# Fake wide compatibility for search tools and personas.
|
||||
min_, max_ = 0, version_int('9999')
|
||||
min_human, max_human = None, None
|
||||
data['appversion'][app.id] = {
|
||||
'min': min_, 'min_human': min_human,
|
||||
'max': max_, 'max_human': max_human,
|
||||
}
|
||||
# Quadruple the boost if the add-on is public.
|
||||
if (obj.status == amo.STATUS_PUBLIC and not obj.is_experimental and
|
||||
'boost' in data):
|
||||
|
@ -203,25 +232,22 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
# transformer that sets it.
|
||||
data['category'] = [cat.id for cat in obj.all_categories]
|
||||
if obj.current_version:
|
||||
data['current_version'] = {
|
||||
'id': obj.current_version.pk,
|
||||
'files': [{
|
||||
'id': file_.id,
|
||||
'created': file_.created,
|
||||
'filename': file_.filename,
|
||||
'hash': file_.hash,
|
||||
'platform': file_.platform,
|
||||
'size': file_.size,
|
||||
'status': file_.status,
|
||||
} for file_ in obj.current_version.all_files],
|
||||
'reviewed': obj.current_version.reviewed,
|
||||
'version': obj.current_version.version,
|
||||
}
|
||||
# FIXME: remove `appversion` once the newest mapping that has
|
||||
# 'current_version.compatible_apps` is live.
|
||||
data['appversion'] = cls.extract_compatibility_info(
|
||||
obj.current_version)
|
||||
data['current_version'] = cls.extract_version(
|
||||
obj, obj.current_version)
|
||||
data['has_version'] = True
|
||||
data['platforms'] = [p.id for p in
|
||||
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['listed_authors'] = [
|
||||
{'name': a.name, 'id': a.id, 'username': a.username}
|
||||
for a in obj.listed_authors
|
||||
|
|
|
@ -1184,7 +1184,7 @@ class Addon(OnChangeMixin, ModelBase):
|
|||
def show_adu(self):
|
||||
return self.type != amo.ADDON_SEARCH
|
||||
|
||||
@amo.cached_property
|
||||
@amo.cached_property(writable=True)
|
||||
def current_beta_version(self):
|
||||
"""Retrieves the latest version of an addon, in the beta channel."""
|
||||
versions = self.versions.filter(files__status=amo.STATUS_BETA)[:1]
|
||||
|
|
|
@ -145,6 +145,7 @@ class AddonEulaPolicySerializer(serializers.ModelSerializer):
|
|||
class AddonSerializer(serializers.ModelSerializer):
|
||||
authors = AddonAuthorSerializer(many=True, source='listed_authors')
|
||||
categories = serializers.SerializerMethodField()
|
||||
current_beta_version = SimpleVersionSerializer()
|
||||
current_version = SimpleVersionSerializer()
|
||||
description = TranslationSerializerField()
|
||||
edit_url = serializers.SerializerMethodField()
|
||||
|
@ -173,6 +174,7 @@ class AddonSerializer(serializers.ModelSerializer):
|
|||
'authors',
|
||||
'average_daily_users',
|
||||
'categories',
|
||||
'current_beta_version',
|
||||
'current_version',
|
||||
'default_locale',
|
||||
'description',
|
||||
|
@ -289,6 +291,34 @@ class ESAddonSerializer(BaseESSerializer, AddonSerializer):
|
|||
translated_fields = ('name', 'description', 'homepage', 'summary',
|
||||
'support_email', 'support_url')
|
||||
|
||||
def fake_version_object(self, obj, data):
|
||||
if data:
|
||||
version = Version(
|
||||
addon=obj, id=data['id'],
|
||||
reviewed=self.handle_date(data['reviewed']),
|
||||
version=data['version'])
|
||||
version.all_files = [
|
||||
File(
|
||||
id=file_['id'], created=self.handle_date(file_['created']),
|
||||
hash=file_['hash'], filename=file_['filename'],
|
||||
platform=file_['platform'], size=file_['size'],
|
||||
status=file_['status'], version=version)
|
||||
for file_ in data.get('files', [])
|
||||
]
|
||||
|
||||
# In ES we store integers for the appversion info, we need to
|
||||
# convert it back to strings.
|
||||
compatible_apps = {}
|
||||
for app_id, compat_dict in data.get('compatible_apps', {}).items():
|
||||
app_name = APPS_ALL[int(app_id)]
|
||||
compatible_apps[app_name] = ApplicationsVersions(
|
||||
min=AppVersion(version=compat_dict.get('min_human', '')),
|
||||
max=AppVersion(version=compat_dict.get('max_human', '')))
|
||||
version.compatible_apps = compatible_apps
|
||||
else:
|
||||
version = None
|
||||
return version
|
||||
|
||||
def fake_object(self, data):
|
||||
"""Create a fake instance of Addon and related models from ES data."""
|
||||
obj = Addon(id=data['id'], slug=data['slug'])
|
||||
|
@ -328,33 +358,14 @@ class ESAddonSerializer(BaseESSerializer, AddonSerializer):
|
|||
# Attach translations (they require special treatment).
|
||||
self._attach_translations(obj, data, self.translated_fields)
|
||||
|
||||
# Attach related models (also faking them).
|
||||
data_version = data.get('current_version')
|
||||
if data_version:
|
||||
obj._current_version = Version(
|
||||
addon=obj, id=data_version['id'],
|
||||
reviewed=self.handle_date(data_version['reviewed']),
|
||||
version=data_version['version'])
|
||||
data_files = data_version.get('files', [])
|
||||
obj._current_version.all_files = [
|
||||
File(
|
||||
id=file_['id'], created=self.handle_date(file_['created']),
|
||||
hash=file_['hash'], filename=file_['filename'],
|
||||
platform=file_['platform'], size=file_['size'],
|
||||
status=file_['status'], version=obj._current_version)
|
||||
for file_ in data_files
|
||||
]
|
||||
|
||||
# In ES we store integers for the appversion info, we need to
|
||||
# convert it back to strings.
|
||||
compatible_apps = {}
|
||||
for app_id, compat_dict in data['appversion'].items():
|
||||
app_name = APPS_ALL[int(app_id)]
|
||||
compatible_apps[app_name] = ApplicationsVersions(
|
||||
min=AppVersion(version=compat_dict.get('min_human', '')),
|
||||
max=AppVersion(version=compat_dict.get('max_human', '')))
|
||||
|
||||
obj._current_version.compatible_apps = compatible_apps
|
||||
# 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.
|
||||
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'))
|
||||
|
||||
data_authors = data.get('listed_authors', [])
|
||||
obj.listed_authors = [
|
||||
|
|
|
@ -3,7 +3,8 @@ from itertools import chain
|
|||
|
||||
from olympia import amo
|
||||
from olympia.amo.models import SearchMixin
|
||||
from olympia.amo.tests import addon_factory, ESTestCase, file_factory, TestCase
|
||||
from olympia.amo.tests import (
|
||||
addon_factory, ESTestCase, file_factory, TestCase, version_factory)
|
||||
from olympia.addons.models import (
|
||||
Addon, attach_tags, attach_translations, Preview)
|
||||
from olympia.addons.indexers import AddonIndexer
|
||||
|
@ -44,8 +45,8 @@ class TestAddonIndexer(TestCase):
|
|||
# exist on the model, or it has a different name, or the value we need
|
||||
# to store in ES differs from the one in the db.
|
||||
complex_fields = [
|
||||
'app', 'appversion', 'boost', 'category', 'current_version',
|
||||
'description', 'has_eula', 'has_privacy_policy',
|
||||
'app', 'appversion', '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',
|
||||
|
@ -105,7 +106,8 @@ class TestAddonIndexer(TestCase):
|
|||
# Make sure current_version mapping is set.
|
||||
assert mapping_properties['current_version']['properties']
|
||||
version_mapping = mapping_properties['current_version']['properties']
|
||||
expected_version_keys = ('id', 'files', 'reviewed', 'version')
|
||||
expected_version_keys = (
|
||||
'id', 'compatible_apps', 'files', 'reviewed', 'version')
|
||||
assert set(version_mapping.keys()) == set(expected_version_keys)
|
||||
|
||||
# Make sure files mapping is set inside current_version.
|
||||
|
@ -145,6 +147,8 @@ class TestAddonIndexer(TestCase):
|
|||
}
|
||||
assert extracted['boost'] == self.addon.average_daily_users ** .2 * 4
|
||||
assert extracted['category'] == [1, 22, 71] # From fixture.
|
||||
assert extracted['current_beta_version'] is None
|
||||
assert extracted['current_version']
|
||||
assert extracted['has_theme_rereview'] is None
|
||||
assert extracted['listed_authors'] == [
|
||||
{'name': u'55021 التطب', 'id': 55021, 'username': '55021'}]
|
||||
|
@ -169,12 +173,22 @@ 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)
|
||||
extracted = self._extract()
|
||||
|
||||
assert extracted['current_version']
|
||||
assert extracted['current_version']['id'] == version.pk
|
||||
assert extracted['current_version']['compatible_apps'] == {
|
||||
FIREFOX.id: {
|
||||
'min': 2000000200100L,
|
||||
'max': 4000000200100L,
|
||||
'max_human': '4.0',
|
||||
'min_human': '2.0',
|
||||
}
|
||||
}
|
||||
assert extracted['current_version']['reviewed'] == version.reviewed
|
||||
assert extracted['current_version']['version'] == version.version
|
||||
for index, file_ in enumerate(version.all_files):
|
||||
|
@ -186,9 +200,31 @@ class TestAddonIndexer(TestCase):
|
|||
assert extracted_file['platform'] == file_.platform
|
||||
assert extracted_file['size'] == file_.size
|
||||
assert extracted_file['status'] == file_.status
|
||||
|
||||
assert extracted['has_version']
|
||||
assert set(extracted['platforms']) == set([PLATFORM_MAC.id,
|
||||
PLATFORM_ALL.id])
|
||||
version = current_beta_version
|
||||
assert extracted['current_beta_version']
|
||||
assert extracted['current_beta_version']['id'] == version.pk
|
||||
assert extracted['current_beta_version']['compatible_apps'] == {
|
||||
FIREFOX.id: {
|
||||
'min': 4009900200100L,
|
||||
'max': 5009900200100L,
|
||||
'max_human': '5.0.99',
|
||||
'min_human': '4.0.99',
|
||||
}
|
||||
}
|
||||
assert extracted['current_beta_version']['version'] == version.version
|
||||
for index, file_ in enumerate(version.all_files):
|
||||
extracted_file = extracted['current_beta_version']['files'][index]
|
||||
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 = {
|
||||
|
|
|
@ -5,7 +5,8 @@ from rest_framework.test import APIRequestFactory
|
|||
from olympia import amo
|
||||
from olympia.amo.helpers import absolutify
|
||||
from olympia.amo.tests import (
|
||||
addon_factory, ESTestCase, file_factory, TestCase, user_factory)
|
||||
addon_factory, ESTestCase, file_factory, TestCase, version_factory,
|
||||
user_factory)
|
||||
from olympia.amo.urlresolvers import reverse
|
||||
from olympia.addons.indexers import AddonIndexer
|
||||
from olympia.addons.models import (
|
||||
|
@ -23,6 +24,37 @@ class AddonSerializerOutputTestMixin(object):
|
|||
def setUp(self):
|
||||
self.request = APIRequestFactory().get('/')
|
||||
|
||||
def _test_version(self, version, data):
|
||||
assert data['id'] == version.pk
|
||||
|
||||
assert data['compatibility']
|
||||
assert len(data['compatibility']) == len(version.compatible_apps)
|
||||
for app, compat in version.compatible_apps.items():
|
||||
assert data['compatibility'][app.short] == {
|
||||
'min': compat.min.version,
|
||||
'max': compat.max.version
|
||||
}
|
||||
assert data['files']
|
||||
assert len(data['files']) == 1
|
||||
|
||||
result_file = data['files'][0]
|
||||
file_ = version.files.latest('pk')
|
||||
assert result_file['id'] == file_.pk
|
||||
assert result_file['created'] == file_.created.isoformat()
|
||||
assert result_file['hash'] == file_.hash
|
||||
assert result_file['platform'] == (
|
||||
amo.PLATFORM_CHOICES_API[file_.platform])
|
||||
assert result_file['size'] == file_.size
|
||||
assert result_file['status'] == amo.STATUS_CHOICES_API[file_.status]
|
||||
assert result_file['url'] == file_.get_url_path(src='')
|
||||
|
||||
assert data['edit_url'] == absolutify(
|
||||
self.addon.get_dev_url(
|
||||
'versions.edit', args=[version.pk], prefix_only=True))
|
||||
assert data['reviewed'] == version.reviewed
|
||||
assert data['version'] == version.version
|
||||
assert data['url'] == absolutify(version.get_url_path())
|
||||
|
||||
def test_basic(self):
|
||||
self.addon = addon_factory(
|
||||
average_daily_users=4242,
|
||||
|
@ -68,6 +100,8 @@ class AddonSerializerOutputTestMixin(object):
|
|||
ApplicationsVersions.objects.get_or_create(
|
||||
application=amo.THUNDERBIRD.id, version=self.addon.current_version,
|
||||
min=av_min, max=av_max)
|
||||
# Reset current_version.compatible_apps now that we've added an app.
|
||||
del self.addon.current_version.compatible_apps
|
||||
|
||||
cat1 = Category.from_static_category(
|
||||
CATEGORIES[amo.FIREFOX.id][amo.ADDON_EXTENSION]['bookmarks'])
|
||||
|
@ -83,8 +117,6 @@ class AddonSerializerOutputTestMixin(object):
|
|||
AddonCategory.objects.create(addon=self.addon, category=cat3)
|
||||
|
||||
result = self.serialize()
|
||||
version = self.addon.current_version
|
||||
file_ = version.files.latest('pk')
|
||||
|
||||
assert result['id'] == self.addon.pk
|
||||
|
||||
|
@ -93,32 +125,11 @@ class AddonSerializerOutputTestMixin(object):
|
|||
'firefox': ['alerts-updates', 'bookmarks'],
|
||||
'thunderbird': ['calendar']}
|
||||
|
||||
assert result['current_beta_version'] is None
|
||||
|
||||
assert result['current_version']
|
||||
assert result['current_version']['id'] == version.pk
|
||||
assert result['current_version']['compatibility'] == {
|
||||
'firefox': {'max': u'5.0.99', 'min': u'4.0.99'},
|
||||
'thunderbird': {'max': u'3.0.99', 'min': u'2.0.99'}
|
||||
}
|
||||
assert result['current_version']['files']
|
||||
assert len(result['current_version']['files']) == 1
|
||||
|
||||
result_file = result['current_version']['files'][0]
|
||||
assert result_file['id'] == file_.pk
|
||||
assert result_file['created'] == file_.created.isoformat()
|
||||
assert result_file['hash'] == file_.hash
|
||||
assert result_file['platform'] == 'windows'
|
||||
assert result_file['size'] == file_.size
|
||||
assert result_file['status'] == 'public'
|
||||
assert result_file['url'] == file_.get_url_path(src='')
|
||||
|
||||
assert result['current_version']['edit_url'] == absolutify(
|
||||
self.addon.get_dev_url(
|
||||
'versions.edit', args=[self.addon.current_version.pk],
|
||||
prefix_only=True))
|
||||
assert result['current_version']['reviewed'] == version.reviewed
|
||||
assert result['current_version']['version'] == version.version
|
||||
assert result['current_version']['url'] == absolutify(
|
||||
version.get_url_path())
|
||||
self._test_version(
|
||||
self.addon.current_version, result['current_version'])
|
||||
|
||||
assert result['authors']
|
||||
assert len(result['authors']) == 2
|
||||
|
@ -186,6 +197,23 @@ class AddonSerializerOutputTestMixin(object):
|
|||
|
||||
return result
|
||||
|
||||
def test_current_beta_version(self):
|
||||
self.addon = addon_factory()
|
||||
|
||||
self.beta_version = version_factory(
|
||||
addon=self.addon, file_kw={'status': amo.STATUS_BETA},
|
||||
version='1.1beta')
|
||||
|
||||
result = self.serialize()
|
||||
assert result['current_beta_version']
|
||||
self._test_version(self.beta_version, result['current_beta_version'], )
|
||||
|
||||
# Just in case, test that current version is still present & different.
|
||||
assert result['current_version']
|
||||
assert result['current_version'] != result['current_beta_version']
|
||||
self._test_version(
|
||||
self.addon.current_version, result['current_version'])
|
||||
|
||||
def test_is_disabled(self):
|
||||
self.addon = addon_factory(disabled_by_user=True)
|
||||
result = self.serialize()
|
||||
|
|
|
@ -1944,6 +1944,10 @@ class TestVersionViewSetList(AddonAndVersionViewSetDetailMixin, TestCase):
|
|||
self._test_url_only_contains_old_version(filter='all')
|
||||
self._test_url_only_contains_old_version(filter='all_with_deleted')
|
||||
|
||||
def test_beta_version(self):
|
||||
self.old_version.files.update(status=amo.STATUS_BETA)
|
||||
self._test_url_only_contains_old_version(filter='beta_only')
|
||||
|
||||
|
||||
class TestAddonViewSetFeatureCompatibility(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -703,7 +703,7 @@ class AddonVersionViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
|
|||
#
|
||||
# When accessing a single version or if requesting it explicitly when
|
||||
# listing, admins can access all versions, including deleted ones.
|
||||
can_access_all_versions_included_deleted = (
|
||||
should_access_all_versions_included_deleted = (
|
||||
(requested == 'all_with_deleted' or self.action != 'list') and
|
||||
self.request.user.is_authenticated() and
|
||||
self.request.user.is_staff)
|
||||
|
@ -711,16 +711,23 @@ class AddonVersionViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
|
|||
# When accessing a single version or if requesting it explicitly when
|
||||
# listing, reviewers and add-on authors can access all non-deleted
|
||||
# versions.
|
||||
can_access_all_versions = (
|
||||
should_access_all_versions = (
|
||||
(requested == 'all' or self.action != 'list') and
|
||||
(AllowReviewer().has_permission(self.request, self) or
|
||||
AllowAddonAuthor().has_object_permission(
|
||||
self.request, self, self.get_addon_object())))
|
||||
|
||||
if can_access_all_versions_included_deleted:
|
||||
# Everyone can see (non deleted) beta version when they request it
|
||||
# explicitly.
|
||||
should_access_only_beta_versions = (requested == 'beta_only')
|
||||
|
||||
if should_access_all_versions_included_deleted:
|
||||
self.queryset = Version.unfiltered.all()
|
||||
elif can_access_all_versions:
|
||||
elif should_access_all_versions:
|
||||
self.queryset = Version.objects.all()
|
||||
elif should_access_only_beta_versions:
|
||||
self.queryset = Version.objects.filter(
|
||||
files__status=amo.STATUS_BETA).distinct()
|
||||
|
||||
# Now that the base queryset has been altered, call super() to use it.
|
||||
qs = super(AddonVersionViewSet, self).get_queryset()
|
||||
|
|
|
@ -88,8 +88,10 @@ class AddonAppVersionFilterParam(AddonFilterParam):
|
|||
def get_es_filter(self):
|
||||
app_id, low, high = self.get_values()
|
||||
return [
|
||||
F('range', **{'appversion.%d.min' % app_id: {'lte': low}}),
|
||||
F('range', **{'appversion.%d.max' % app_id: {'gte': high}}),
|
||||
F('range', **{'current_version.compatible_apps.%d.min' % app_id:
|
||||
{'lte': low}}),
|
||||
F('range', **{'current_version.compatible_apps.%d.max' % app_id:
|
||||
{'gte': high}}),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -231,8 +231,10 @@ class TestSearchParameterFilter(FilterTestsBase):
|
|||
'app': 'firefox'})
|
||||
must = qs['query']['filtered']['filter']['bool']['must']
|
||||
assert {'term': {'app': amo.FIREFOX.id}} in must
|
||||
assert {'range': {'appversion.1.min': {'lte': 46000000200100}}} in must
|
||||
assert {'range': {'appversion.1.max': {'gte': 46000000000100}}} in must
|
||||
assert {'range': {'current_version.compatible_apps.1.min':
|
||||
{'lte': 46000000200100}}} in must
|
||||
assert {'range': {'current_version.compatible_apps.1.max':
|
||||
{'gte': 46000000000100}}} in must
|
||||
|
||||
def test_search_by_platform_invalid(self):
|
||||
qs = self._filter(data={'platform': unicode(amo.PLATFORM_WIN.id + 42)})
|
||||
|
|
Загрузка…
Ссылка в новой задаче