Remove CompatOverride models and api (#12086)
This commit is contained in:
Родитель
2963e41224
Коммит
0e7152107c
|
@ -485,38 +485,6 @@ This endpoint returns a list of suggested replacements for legacy add-ons that a
|
|||
:>json string results[].replacement[]: An array of guids for the replacements add-ons. If there is a direct replacement this will be a list of one add-on guid. The list can be empty if all the replacement add-ons are invalid (e.g. not publicly available on AMO). The list will also be empty if the replacement is to a url that is not an addon or collection.
|
||||
|
||||
|
||||
---------------
|
||||
Compat Override
|
||||
---------------
|
||||
|
||||
.. _addon-compat-override:
|
||||
|
||||
This endpoint allows compatibility overrides specified by AMO admins to be searched.
|
||||
Compatibilty overrides are used within Firefox i(and other toolkit applications e.g. Thunderbird) to change compatibility of installed add-ons where they have stopped working correctly in new release of Firefox, etc.
|
||||
|
||||
.. http:get:: /api/v4/addons/compat-override/
|
||||
|
||||
:query string guid: Filter by exact add-on guid. Multiple guids can be specified, separated by comma(s), in which case any add-ons matching any of the guids will be returned. As guids are unique there should be at most one add-on result per guid specified.
|
||||
:query int page: 1-based page number. Defaults to 1.
|
||||
:query int page_size: Maximum number of results to return for the requested page. Defaults to 25.
|
||||
:>json int count: The number of results for this query.
|
||||
:>json string next: The URL of the next page of results.
|
||||
:>json string previous: The URL of the previous page of results.
|
||||
:>json array results: An array of compat overrides.
|
||||
:>json int|null results[].addon_id: The add-on identifier on AMO, if specified.
|
||||
:>json string results[].addon_guid: The add-on extension identifier.
|
||||
:>json string results[].name: A description entered by AMO admins to describe the override.
|
||||
:>json array results[].version_ranges: An array of affected versions of the add-on.
|
||||
:>json string results[].version_ranges[].addon_min_version: minimum version of the add-on to be disabled.
|
||||
:>json string results[].version_ranges[].addon_max_version: maximum version of the add-on to be disabled.
|
||||
:>json array results[].version_ranges[].applications: An array of affected applications for this range of versions.
|
||||
:>json string results[].version_ranges[].applications[].name: Application name (e.g. Firefox).
|
||||
:>json int results[].version_ranges[].applications[].id: Application id on AMO.
|
||||
:>json string results[].version_ranges[].applications[].min_version: minimum version of the application to be disabled in.
|
||||
:>json string results[].version_ranges[].applications[].max_version: maximum version of the application to be disabled in.
|
||||
:>json string results[].version_ranges[].applications[].guid: Application `guid <https://addons.mozilla.org/en-US/firefox/pages/appversions/>`_.
|
||||
|
||||
|
||||
---------------
|
||||
Recommendations
|
||||
---------------
|
||||
|
|
|
@ -336,6 +336,8 @@ v4 API changelog
|
|||
* 2019-08-08: add secondary shelf to /hero/ endpoint. https://github.com/mozilla/addons-server/issues/11779
|
||||
* 2019-08-15: dropped support for LWT specific statuses.
|
||||
* 2019-08-15: added promo modules to secondary hero shelves. https://github.com/mozilla/addons-server/issues/11780
|
||||
* 2019-08-15: removed /addons/compat-override/ from v4 and above. Still exists in /v3/ but will always return an empty response. https://github.com/mozilla/addons-server/issues/12063
|
||||
|
||||
|
||||
----------------
|
||||
v5 API changelog
|
||||
|
|
|
@ -151,18 +151,6 @@ class Update(object):
|
|||
data['d2c_min_version'] = version_int(d2c_min)
|
||||
sql.append("AND appmax.version_int >= %(d2c_min_version)s ")
|
||||
|
||||
# Filter out versions found in compat overrides
|
||||
sql.append("""AND
|
||||
NOT versions.id IN (
|
||||
SELECT version_id FROM incompatible_versions
|
||||
WHERE app_id=%(app_id)s AND
|
||||
(min_app_version='0' AND
|
||||
max_app_version_int >= %(version_int)s) OR
|
||||
(min_app_version_int <= %(version_int)s AND
|
||||
max_app_version='*') OR
|
||||
(min_app_version_int <= %(version_int)s AND
|
||||
max_app_version_int >= %(version_int)s)) """)
|
||||
|
||||
else: # Not defined or 'strict'.
|
||||
sql.append('AND appmax.version_int >= %(version_int)s ')
|
||||
|
||||
|
|
|
@ -123,26 +123,6 @@ class FrozenAddonAdmin(admin.ModelAdmin):
|
|||
raw_id_fields = ('addon',)
|
||||
|
||||
|
||||
class CompatOverrideRangeInline(admin.TabularInline):
|
||||
model = models.CompatOverrideRange
|
||||
# Exclude type since firefox only supports blocking right now.
|
||||
exclude = ('type',)
|
||||
|
||||
|
||||
class CompatOverrideAdminForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
if '_confirm' in self.data:
|
||||
raise forms.ValidationError('Click "Save" to confirm changes.')
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class CompatOverrideAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('addon',)
|
||||
inlines = [CompatOverrideRangeInline]
|
||||
form = CompatOverrideAdminForm
|
||||
|
||||
|
||||
class ReplacementAddonForm(forms.ModelForm):
|
||||
def clean_path(self):
|
||||
path = None
|
||||
|
@ -205,5 +185,4 @@ class ReplacementAddonAdmin(admin.ModelAdmin):
|
|||
admin.site.register(models.DeniedGuid)
|
||||
admin.site.register(models.Addon, AddonAdmin)
|
||||
admin.site.register(models.FrozenAddon, FrozenAddonAdmin)
|
||||
admin.site.register(models.CompatOverride, CompatOverrideAdmin)
|
||||
admin.site.register(models.ReplacementAddon, ReplacementAddonAdmin)
|
||||
|
|
|
@ -21,7 +21,7 @@ sub_versions = NestedSimpleRouter(sub_addons, r'versions', lookup='version')
|
|||
sub_versions.register(r'reviewnotes', VersionReviewNotesViewSet,
|
||||
basename='version-reviewnotes')
|
||||
|
||||
urlpatterns = [
|
||||
urls = [
|
||||
url(r'', include(addons.urls)),
|
||||
url(r'', include(sub_addons.urls)),
|
||||
url(r'', include(sub_versions.urls)),
|
||||
|
@ -34,8 +34,13 @@ urlpatterns = [
|
|||
name='addon-language-tools'),
|
||||
url(r'^replacement-addon/$', ReplacementAddonView.as_view(),
|
||||
name='addon-replacement-addon'),
|
||||
url(r'^compat-override/$', CompatOverrideView.as_view(),
|
||||
name='addon-compat-override'),
|
||||
|
||||
url(r'^recommendations/$', AddonRecommendationView.as_view(),
|
||||
name='addon-recommendations'),
|
||||
]
|
||||
|
||||
addons_v3 = urls + [
|
||||
url(r'^compat-override/$', CompatOverrideView.as_view(),
|
||||
name='addon-compat-override')]
|
||||
|
||||
addons_v4 = urls
|
||||
|
|
|
@ -37,7 +37,7 @@ from olympia.amo.models import (
|
|||
from olympia.amo.templatetags import jinja_helpers
|
||||
from olympia.amo.urlresolvers import reverse
|
||||
from olympia.amo.utils import (
|
||||
StopWatch, attach_trans_dict, chunked,
|
||||
StopWatch, attach_trans_dict,
|
||||
find_language, send_mail, slugify, sorted_groupby, timer, to_language)
|
||||
from olympia.constants.categories import CATEGORIES, CATEGORIES_BY_ID
|
||||
from olympia.constants.reviewers import REPUTATION_CHOICES
|
||||
|
@ -1817,154 +1817,6 @@ def freezer(sender, instance, **kw):
|
|||
Addon.objects.get(id=instance.addon_id).update(hotness=0)
|
||||
|
||||
|
||||
class CompatOverride(ModelBase):
|
||||
"""Helps manage compat info for add-ons not hosted on AMO."""
|
||||
id = PositiveAutoField(primary_key=True)
|
||||
name = models.CharField(max_length=255, blank=True, null=True)
|
||||
guid = models.CharField(max_length=255, unique=True)
|
||||
addon = models.ForeignKey(
|
||||
Addon, blank=True, null=True, on_delete=models.CASCADE,
|
||||
help_text='Fill this out to link an override to a hosted add-on')
|
||||
|
||||
class Meta:
|
||||
db_table = 'compat_override'
|
||||
unique_together = ('addon', 'guid')
|
||||
|
||||
def save(self, *args, **kw):
|
||||
if not self.addon:
|
||||
qs = Addon.objects.filter(guid=self.guid)
|
||||
if qs:
|
||||
self.addon = qs[0]
|
||||
return super(CompatOverride, self).save(*args, **kw)
|
||||
|
||||
def __str__(self):
|
||||
if self.addon:
|
||||
return str(self.addon)
|
||||
elif self.name:
|
||||
return '%s (%s)' % (self.name, self.guid)
|
||||
else:
|
||||
return self.guid
|
||||
|
||||
@staticmethod
|
||||
def transformer(overrides):
|
||||
if not overrides:
|
||||
return
|
||||
|
||||
id_map = {override.id: override for override in overrides}
|
||||
qs = CompatOverrideRange.objects.filter(compat__in=id_map)
|
||||
|
||||
for compat_id, ranges in sorted_groupby(qs, 'compat_id'):
|
||||
id_map[compat_id].compat_ranges = list(ranges)
|
||||
|
||||
# May be filled in by a transformer for performance.
|
||||
@cached_property
|
||||
def compat_ranges(self):
|
||||
return list(self._compat_ranges.all())
|
||||
|
||||
def collapsed_ranges(self):
|
||||
"""Collapse identical version ranges into one entity."""
|
||||
Range = collections.namedtuple('Range', 'type min max apps')
|
||||
AppRange = collections.namedtuple('AppRange', 'app min max')
|
||||
rv = []
|
||||
|
||||
def sort_key(x):
|
||||
return (x.min_version, x.max_version, x.type)
|
||||
|
||||
for key, compats in sorted_groupby(self.compat_ranges, key=sort_key):
|
||||
compats = list(compats)
|
||||
first = compats[0]
|
||||
item = Range(first.override_type(), first.min_version,
|
||||
first.max_version, [])
|
||||
for compat in compats:
|
||||
app = AppRange(amo.APPS_ALL[compat.app],
|
||||
compat.min_app_version, compat.max_app_version)
|
||||
item.apps.append(app)
|
||||
rv.append(item)
|
||||
return rv
|
||||
|
||||
|
||||
OVERRIDE_TYPES = (
|
||||
(0, 'Compatible (not supported)'),
|
||||
(1, 'Incompatible'),
|
||||
)
|
||||
|
||||
|
||||
class CompatOverrideRange(ModelBase):
|
||||
"""App compatibility for a certain version range of a RemoteAddon."""
|
||||
id = PositiveAutoField(primary_key=True)
|
||||
compat = models.ForeignKey(
|
||||
CompatOverride, related_name='_compat_ranges',
|
||||
on_delete=models.CASCADE)
|
||||
type = models.SmallIntegerField(choices=OVERRIDE_TYPES, default=1)
|
||||
min_version = models.CharField(
|
||||
max_length=255, default='0',
|
||||
help_text=u'If not "0", version is required to exist for the override'
|
||||
u' to take effect.')
|
||||
max_version = models.CharField(
|
||||
max_length=255, default='*',
|
||||
help_text=u'If not "*", version is required to exist for the override'
|
||||
u' to take effect.')
|
||||
app = models.PositiveIntegerField(choices=amo.APPS_CHOICES,
|
||||
db_column='app_id')
|
||||
min_app_version = models.CharField(max_length=255, default='0')
|
||||
max_app_version = models.CharField(max_length=255, default='*')
|
||||
|
||||
class Meta:
|
||||
db_table = 'compat_override_range'
|
||||
|
||||
def override_type(self):
|
||||
"""This is what Firefox wants to see in the XML output."""
|
||||
return {0: 'compatible', 1: 'incompatible'}[self.type]
|
||||
|
||||
|
||||
class IncompatibleVersions(ModelBase):
|
||||
"""
|
||||
Denormalized table to join against for fast compat override filtering.
|
||||
|
||||
This was created to be able to join against a specific version record since
|
||||
the CompatOverrideRange can be wildcarded (e.g. 0 to *, or 1.0 to 1.*), and
|
||||
addon versioning isn't as consistent as Firefox versioning to trust
|
||||
`version_int` in all cases. So extra logic needed to be provided for when
|
||||
a particular version falls within the range of a compatibility override.
|
||||
"""
|
||||
id = PositiveAutoField(primary_key=True)
|
||||
version = models.ForeignKey(
|
||||
Version, related_name='+', on_delete=models.CASCADE)
|
||||
app = models.PositiveIntegerField(choices=amo.APPS_CHOICES,
|
||||
db_column='app_id')
|
||||
min_app_version = models.CharField(max_length=255, blank=True, default='0')
|
||||
max_app_version = models.CharField(max_length=255, blank=True, default='*')
|
||||
min_app_version_int = models.BigIntegerField(blank=True, null=True,
|
||||
editable=False, db_index=True)
|
||||
max_app_version_int = models.BigIntegerField(blank=True, null=True,
|
||||
editable=False, db_index=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'incompatible_versions'
|
||||
|
||||
def __str__(self):
|
||||
return u'<IncompatibleVersion V:%s A:%s %s-%s>' % (
|
||||
self.version.id, self.app.id, self.min_app_version,
|
||||
self.max_app_version)
|
||||
|
||||
def save(self, *args, **kw):
|
||||
self.min_app_version_int = version_int(self.min_app_version)
|
||||
self.max_app_version_int = version_int(self.max_app_version)
|
||||
return super(IncompatibleVersions, self).save(*args, **kw)
|
||||
|
||||
|
||||
def update_incompatible_versions(sender, instance, **kw):
|
||||
if not instance.compat.addon_id:
|
||||
return
|
||||
if not instance.compat.addon.type == amo.ADDON_EXTENSION:
|
||||
return
|
||||
|
||||
from . import tasks
|
||||
versions = instance.compat.addon.versions.values_list('id', flat=True)
|
||||
for chunk in chunked(versions, 50):
|
||||
tasks.update_incompatible_appversions.delay(chunk)
|
||||
|
||||
|
||||
class ReplacementAddon(ModelBase):
|
||||
guid = models.CharField(max_length=255, unique=True, null=True)
|
||||
path = models.CharField(max_length=255, null=True,
|
||||
|
@ -1982,14 +1834,6 @@ class ReplacementAddon(ModelBase):
|
|||
return self.path_is_external(self.path)
|
||||
|
||||
|
||||
models.signals.post_save.connect(update_incompatible_versions,
|
||||
sender=CompatOverrideRange,
|
||||
dispatch_uid='cor_update_incompatible')
|
||||
models.signals.post_delete.connect(update_incompatible_versions,
|
||||
sender=CompatOverrideRange,
|
||||
dispatch_uid='cor_update_incompatible')
|
||||
|
||||
|
||||
def track_new_status(sender, instance, *args, **kw):
|
||||
if kw.get('raw'):
|
||||
# The addon is being loaded from a fixure.
|
||||
|
|
|
@ -24,8 +24,7 @@ from olympia.users.models import UserProfile
|
|||
from olympia.versions.models import (
|
||||
ApplicationsVersions, License, Version, VersionPreview)
|
||||
|
||||
from .models import (
|
||||
Addon, CompatOverride, Preview, ReplacementAddon, attach_tags)
|
||||
from .models import Addon, Preview, ReplacementAddon, attach_tags
|
||||
|
||||
|
||||
class FileSerializer(serializers.ModelSerializer):
|
||||
|
@ -748,30 +747,3 @@ class ReplacementAddonSerializer(serializers.ModelSerializer):
|
|||
return self._get_collection_guids(
|
||||
coll_match.group('user_id'), coll_match.group('coll_slug'))
|
||||
return []
|
||||
|
||||
|
||||
class CompatOverrideSerializer(serializers.ModelSerializer):
|
||||
|
||||
class VersionRangeSerializer(serializers.Serializer):
|
||||
class ApplicationSerializer(serializers.Serializer):
|
||||
name = serializers.CharField(source='app.pretty')
|
||||
id = serializers.IntegerField(source='app.id')
|
||||
min_version = serializers.CharField(source='min')
|
||||
max_version = serializers.CharField(source='max')
|
||||
guid = serializers.CharField(source='app.guid')
|
||||
|
||||
addon_min_version = serializers.CharField(source='min')
|
||||
addon_max_version = serializers.CharField(source='max')
|
||||
applications = ApplicationSerializer(source='apps', many=True)
|
||||
|
||||
addon_id = serializers.IntegerField()
|
||||
addon_guid = serializers.CharField(source='guid')
|
||||
version_ranges = VersionRangeSerializer(
|
||||
source='collapsed_ranges', many=True)
|
||||
|
||||
class Meta:
|
||||
model = CompatOverride
|
||||
fields = ('addon_id', 'addon_guid', 'name', 'version_ranges')
|
||||
|
||||
def get_addon_id(self, obj):
|
||||
return obj.addon_id
|
||||
|
|
|
@ -15,8 +15,7 @@ import olympia.core
|
|||
from olympia import amo
|
||||
from olympia.addons.indexers import AddonIndexer
|
||||
from olympia.addons.models import (
|
||||
Addon, AddonApprovalsCounter, AppSupport,
|
||||
CompatOverride, IncompatibleVersions, MigratedLWT, Preview,
|
||||
Addon, AddonApprovalsCounter, AppSupport, MigratedLWT, Preview,
|
||||
attach_tags, attach_translations)
|
||||
from olympia.amo.celery import pause_all_tasks, resume_all_tasks, task
|
||||
from olympia.amo.decorators import use_primary_db
|
||||
|
@ -28,7 +27,7 @@ from olympia.lib.es.utils import index_objects
|
|||
from olympia.tags.models import Tag
|
||||
from olympia.users.models import UserProfile
|
||||
from olympia.versions.models import (
|
||||
generate_static_theme_preview, Version, VersionPreview)
|
||||
generate_static_theme_preview, VersionPreview)
|
||||
from olympia.versions.utils import (
|
||||
new_69_theme_properties_from_old, new_theme_version_with_69_properties)
|
||||
|
||||
|
@ -156,86 +155,6 @@ def unindex_addons(ids, **kw):
|
|||
Addon.unindex(addon)
|
||||
|
||||
|
||||
@task
|
||||
def update_incompatible_appversions(data, **kw):
|
||||
"""Updates the incompatible_versions table for this version."""
|
||||
log.info('Updating incompatible_versions for %s versions.' % len(data))
|
||||
|
||||
addon_ids = set()
|
||||
|
||||
for version_id in data:
|
||||
# This is here to handle both post_save and post_delete hooks.
|
||||
IncompatibleVersions.objects.filter(version=version_id).delete()
|
||||
|
||||
try:
|
||||
version = Version.objects.get(pk=version_id)
|
||||
except Version.DoesNotExist:
|
||||
log.info('Version ID [%d] not found. Incompatible versions were '
|
||||
'cleared.' % version_id)
|
||||
return
|
||||
|
||||
addon_ids.add(version.addon_id)
|
||||
|
||||
try:
|
||||
compat = CompatOverride.objects.get(addon=version.addon)
|
||||
except CompatOverride.DoesNotExist:
|
||||
log.info('Compat override for addon with version ID [%d] not '
|
||||
'found. Incompatible versions were cleared.' % version_id)
|
||||
return
|
||||
|
||||
app_ranges = []
|
||||
ranges = compat.collapsed_ranges()
|
||||
|
||||
for range in ranges:
|
||||
if range.min == '0' and range.max == '*':
|
||||
# Wildcard range, add all app ranges
|
||||
app_ranges.extend(range.apps)
|
||||
else:
|
||||
# Since we can't rely on add-on version numbers, get the min
|
||||
# and max ID values and find versions whose ID is within those
|
||||
# ranges, being careful with wildcards.
|
||||
min_id = max_id = None
|
||||
|
||||
if range.min == '0':
|
||||
versions = (Version.objects.filter(addon=version.addon_id)
|
||||
.order_by('id')
|
||||
.values_list('id', flat=True)[:1])
|
||||
if versions:
|
||||
min_id = versions[0]
|
||||
else:
|
||||
try:
|
||||
min_id = Version.objects.get(addon=version.addon_id,
|
||||
version=range.min).id
|
||||
except Version.DoesNotExist:
|
||||
pass
|
||||
|
||||
if range.max == '*':
|
||||
versions = (Version.objects.filter(addon=version.addon_id)
|
||||
.order_by('-id')
|
||||
.values_list('id', flat=True)[:1])
|
||||
if versions:
|
||||
max_id = versions[0]
|
||||
else:
|
||||
try:
|
||||
max_id = Version.objects.get(addon=version.addon_id,
|
||||
version=range.max).id
|
||||
except Version.DoesNotExist:
|
||||
pass
|
||||
|
||||
if min_id and max_id:
|
||||
if min_id <= version.id <= max_id:
|
||||
app_ranges.extend(range.apps)
|
||||
|
||||
for app_range in app_ranges:
|
||||
IncompatibleVersions.objects.create(version=version,
|
||||
app=app_range.app.id,
|
||||
min_app_version=app_range.min,
|
||||
max_app_version=app_range.max)
|
||||
log.info('Added incompatible version for version ID [%d]: '
|
||||
'app:%d, %s -> %s' % (version_id, app_range.app.id,
|
||||
app_range.min, app_range.max))
|
||||
|
||||
|
||||
def make_checksum(header_path):
|
||||
ls = LocalFileStorage()
|
||||
raw_checksum = ls._open(header_path).read()
|
||||
|
|
|
@ -17,8 +17,7 @@ from olympia.activity.models import ActivityLog, AddonLog
|
|||
from olympia.addons import models as addons_models
|
||||
from olympia.addons.models import (
|
||||
Addon, AddonApprovalsCounter, AddonCategory, AddonReviewerFlags, AddonUser,
|
||||
AppSupport, Category, CompatOverride, CompatOverrideRange, DeniedGuid,
|
||||
DeniedSlug, FrozenAddon, IncompatibleVersions, MigratedLWT,
|
||||
AppSupport, Category, DeniedGuid, DeniedSlug, FrozenAddon, MigratedLWT,
|
||||
Preview, ReusedGUID, track_addon_status_change)
|
||||
from olympia.amo.templatetags.jinja_helpers import absolutify
|
||||
from olympia.amo.tests import (
|
||||
|
@ -1738,7 +1737,6 @@ class TestAddonDelete(TestCase):
|
|||
AddonUser.objects.create(
|
||||
addon=addon, user=UserProfile.objects.create())
|
||||
AppSupport.objects.create(addon=addon, app=1)
|
||||
CompatOverride.objects.create(addon=addon)
|
||||
FrozenAddon.objects.create(addon=addon)
|
||||
|
||||
AddonLog.objects.create(
|
||||
|
@ -2469,201 +2467,6 @@ class TestLanguagePack(TestCase, amo.tests.AMOPaths):
|
|||
assert self.addon.reload().get_localepicker() == ''
|
||||
|
||||
|
||||
class TestCompatOverride(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCompatOverride, self).setUp()
|
||||
self.app = amo.APP_IDS[1]
|
||||
|
||||
one = CompatOverride.objects.create(guid='one')
|
||||
CompatOverrideRange.objects.create(compat=one, app=self.app.id)
|
||||
|
||||
two = CompatOverride.objects.create(guid='two')
|
||||
CompatOverrideRange.objects.create(compat=two, app=self.app.id,
|
||||
min_version='1', max_version='2')
|
||||
CompatOverrideRange.objects.create(compat=two, app=self.app.id,
|
||||
min_version='1', max_version='2',
|
||||
min_app_version='3',
|
||||
max_app_version='4')
|
||||
|
||||
def check(self, obj, **kw):
|
||||
"""Check that key/value pairs in kw match attributes of obj."""
|
||||
for key, expected in kw.items():
|
||||
actual = getattr(obj, key)
|
||||
assert actual == expected
|
||||
|
||||
def test_override_type(self):
|
||||
one = CompatOverride.objects.get(guid='one')
|
||||
|
||||
# The default is incompatible.
|
||||
c = CompatOverrideRange.objects.create(compat=one, app=1)
|
||||
assert c.override_type() == 'incompatible'
|
||||
|
||||
c = CompatOverrideRange.objects.create(compat=one, app=1, type=0)
|
||||
assert c.override_type() == 'compatible'
|
||||
|
||||
def test_guid_match(self):
|
||||
# We hook up the add-on automatically if we see a matching guid.
|
||||
addon = Addon.objects.create(id=1, guid='oh yeah', type=1)
|
||||
c = CompatOverride.objects.create(guid=addon.guid)
|
||||
assert c.addon_id == addon.id
|
||||
|
||||
c = CompatOverride.objects.create(guid='something else')
|
||||
assert c.addon is None
|
||||
|
||||
def test_transformer(self):
|
||||
compats = list(CompatOverride.objects
|
||||
.transform(CompatOverride.transformer))
|
||||
ranges = list(CompatOverrideRange.objects.all())
|
||||
# If the transformer works then we won't have any more queries.
|
||||
with self.assertNumQueries(0):
|
||||
for c in compats:
|
||||
assert c.compat_ranges == (
|
||||
[r for r in ranges if r.compat_id == c.id])
|
||||
|
||||
def test_collapsed_ranges(self):
|
||||
# Test that we get back the right structures from collapsed_ranges().
|
||||
c = CompatOverride.objects.get(guid='one')
|
||||
r = c.collapsed_ranges()
|
||||
|
||||
assert len(r) == 1
|
||||
compat_range = r[0]
|
||||
self.check(compat_range, type='incompatible', min='0', max='*')
|
||||
|
||||
assert len(compat_range.apps) == 1
|
||||
self.check(compat_range.apps[0], app=amo.FIREFOX, min='0', max='*')
|
||||
|
||||
def test_collapsed_ranges_multiple_versions(self):
|
||||
c = CompatOverride.objects.get(guid='one')
|
||||
CompatOverrideRange.objects.create(compat=c, app=1,
|
||||
min_version='1', max_version='2',
|
||||
min_app_version='3',
|
||||
max_app_version='3.*')
|
||||
r = c.collapsed_ranges()
|
||||
|
||||
assert len(r) == 2
|
||||
|
||||
self.check(r[0], type='incompatible', min='0', max='*')
|
||||
assert len(r[0].apps) == 1
|
||||
self.check(r[0].apps[0], app=amo.FIREFOX, min='0', max='*')
|
||||
|
||||
self.check(r[1], type='incompatible', min='1', max='2')
|
||||
assert len(r[1].apps) == 1
|
||||
self.check(r[1].apps[0], app=amo.FIREFOX, min='3', max='3.*')
|
||||
|
||||
def test_collapsed_ranges_different_types(self):
|
||||
# If the override ranges have different types they should be separate
|
||||
# entries.
|
||||
c = CompatOverride.objects.get(guid='one')
|
||||
CompatOverrideRange.objects.create(compat=c, app=1, type=0,
|
||||
min_app_version='3',
|
||||
max_app_version='3.*')
|
||||
r = c.collapsed_ranges()
|
||||
|
||||
assert len(r) == 2
|
||||
|
||||
self.check(r[0], type='compatible', min='0', max='*')
|
||||
assert len(r[0].apps) == 1
|
||||
self.check(r[0].apps[0], app=amo.FIREFOX, min='3', max='3.*')
|
||||
|
||||
self.check(r[1], type='incompatible', min='0', max='*')
|
||||
assert len(r[1].apps) == 1
|
||||
self.check(r[1].apps[0], app=amo.FIREFOX, min='0', max='*')
|
||||
|
||||
def test_collapsed_ranges_multiple_apps(self):
|
||||
c = CompatOverride.objects.get(guid='two')
|
||||
r = c.collapsed_ranges()
|
||||
|
||||
assert len(r) == 1
|
||||
compat_range = r[0]
|
||||
self.check(compat_range, type='incompatible', min='1', max='2')
|
||||
|
||||
assert len(compat_range.apps) == 2
|
||||
self.check(compat_range.apps[0], app=amo.FIREFOX, min='0', max='*')
|
||||
self.check(compat_range.apps[1], app=amo.FIREFOX, min='3', max='4')
|
||||
|
||||
def test_collapsed_ranges_multiple_versions_and_apps(self):
|
||||
c = CompatOverride.objects.get(guid='two')
|
||||
CompatOverrideRange.objects.create(min_version='5', max_version='6',
|
||||
compat=c, app=1)
|
||||
r = c.collapsed_ranges()
|
||||
|
||||
assert len(r) == 2
|
||||
self.check(r[0], type='incompatible', min='1', max='2')
|
||||
|
||||
assert len(r[0].apps) == 2
|
||||
self.check(r[0].apps[0], app=amo.FIREFOX, min='0', max='*')
|
||||
self.check(r[0].apps[1], app=amo.FIREFOX, min='3', max='4')
|
||||
|
||||
self.check(r[1], type='incompatible', min='5', max='6')
|
||||
assert len(r[1].apps) == 1
|
||||
self.check(r[1].apps[0], app=amo.FIREFOX, min='0', max='*')
|
||||
|
||||
|
||||
class TestIncompatibleVersions(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestIncompatibleVersions, self).setUp()
|
||||
self.app = amo.APP_IDS[amo.FIREFOX.id]
|
||||
self.addon = Addon.objects.create(guid='r@b', type=amo.ADDON_EXTENSION)
|
||||
|
||||
def test_signals_min(self):
|
||||
assert IncompatibleVersions.objects.count() == 0
|
||||
|
||||
c = CompatOverride.objects.create(guid='r@b')
|
||||
CompatOverrideRange.objects.create(compat=c, app=self.app.id,
|
||||
min_version='0',
|
||||
max_version='1.0')
|
||||
|
||||
# Test the max version matched.
|
||||
version1 = Version.objects.create(id=2, addon=self.addon,
|
||||
version='1.0')
|
||||
assert IncompatibleVersions.objects.filter(
|
||||
version=version1).count() == 1
|
||||
assert IncompatibleVersions.objects.count() == 1
|
||||
|
||||
# Test the lower range.
|
||||
version2 = Version.objects.create(id=1, addon=self.addon,
|
||||
version='0.5')
|
||||
assert IncompatibleVersions.objects.filter(
|
||||
version=version2).count() == 1
|
||||
assert IncompatibleVersions.objects.count() == 2
|
||||
|
||||
# Test delete signals.
|
||||
version1.delete()
|
||||
assert IncompatibleVersions.objects.count() == 1
|
||||
|
||||
version2.delete()
|
||||
assert IncompatibleVersions.objects.count() == 0
|
||||
|
||||
def test_signals_max(self):
|
||||
assert IncompatibleVersions.objects.count() == 0
|
||||
|
||||
c = CompatOverride.objects.create(guid='r@b')
|
||||
CompatOverrideRange.objects.create(compat=c, app=self.app.id,
|
||||
min_version='1.0',
|
||||
max_version='*')
|
||||
|
||||
# Test the min_version matched.
|
||||
version1 = Version.objects.create(addon=self.addon, version='1.0')
|
||||
assert IncompatibleVersions.objects.filter(
|
||||
version=version1).count() == 1
|
||||
assert IncompatibleVersions.objects.count() == 1
|
||||
|
||||
# Test the upper range.
|
||||
version2 = Version.objects.create(addon=self.addon, version='99.0')
|
||||
assert IncompatibleVersions.objects.filter(
|
||||
version=version2).count() == 1
|
||||
assert IncompatibleVersions.objects.count() == 2
|
||||
|
||||
# Test delete signals.
|
||||
version1.delete()
|
||||
assert IncompatibleVersions.objects.count() == 1
|
||||
|
||||
version2.delete()
|
||||
assert IncompatibleVersions.objects.count() == 0
|
||||
|
||||
|
||||
class TestAddonApprovalsCounter(TestCase):
|
||||
def setUp(self):
|
||||
self.addon = addon_factory()
|
||||
|
|
|
@ -7,11 +7,10 @@ from rest_framework.test import APIRequestFactory
|
|||
from olympia import amo
|
||||
from olympia.accounts.tests.test_serializers import TestBaseUserSerializer
|
||||
from olympia.addons.models import (
|
||||
Addon, AddonCategory, AddonUser, Category, CompatOverride,
|
||||
CompatOverrideRange, Preview, ReplacementAddon)
|
||||
Addon, AddonCategory, AddonUser, Category, Preview, ReplacementAddon)
|
||||
from olympia.addons.serializers import (
|
||||
AddonDeveloperSerializer, AddonSerializer, AddonSerializerWithUnlistedData,
|
||||
CompatOverrideSerializer, ESAddonAutoCompleteSerializer, ESAddonSerializer,
|
||||
ESAddonAutoCompleteSerializer, ESAddonSerializer,
|
||||
LanguageToolsSerializer, LicenseSerializer, ReplacementAddonSerializer,
|
||||
SimpleVersionSerializer, VersionSerializer)
|
||||
from olympia.addons.utils import generate_addon_guid
|
||||
|
@ -1336,144 +1335,3 @@ class TestReplacementAddonSerializer(TestCase):
|
|||
addon.update(status=amo.STATUS_APPROVED)
|
||||
result = self.serialize(rep)
|
||||
assert result['replacement'] == [u'newstuff@mozilla']
|
||||
|
||||
|
||||
class TestCompatOverrideSerializer(TestCase):
|
||||
|
||||
def serialize(self, override):
|
||||
serializer = CompatOverrideSerializer()
|
||||
return serializer.to_representation(override)
|
||||
|
||||
def test_linked_addon(self):
|
||||
addon = addon_factory(guid='extrabad@thing')
|
||||
override = CompatOverride.objects.create(
|
||||
name='override with addon', guid=addon.guid, addon=addon)
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=override, app=amo.FIREFOX.id)
|
||||
result = self.serialize(override)
|
||||
|
||||
assert ['addon_guid', 'addon_id', 'name', 'version_ranges'] == sorted(
|
||||
result.keys())
|
||||
assert result['addon_guid'] == 'extrabad@thing'
|
||||
assert result['addon_id'] == addon.id
|
||||
assert result['name'] == 'override with addon'
|
||||
version_range = {
|
||||
'addon_min_version': '0',
|
||||
'addon_max_version': '*',
|
||||
'applications': [{
|
||||
'name': amo.FIREFOX.pretty,
|
||||
'id': amo.FIREFOX.id,
|
||||
'min_version': '0',
|
||||
'max_version': '*',
|
||||
'guid': amo.FIREFOX.guid
|
||||
}]
|
||||
}
|
||||
assert result['version_ranges'] == [version_range]
|
||||
|
||||
def test_no_addon(self):
|
||||
override = CompatOverride.objects.create(
|
||||
name='override', guid='foo@baa')
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=override, app=amo.FIREFOX.id)
|
||||
result = self.serialize(override)
|
||||
|
||||
assert ['addon_guid', 'addon_id', 'name', 'version_ranges'] == sorted(
|
||||
result.keys())
|
||||
assert result['addon_guid'] == 'foo@baa'
|
||||
assert result['addon_id'] is None
|
||||
assert result['name'] == 'override'
|
||||
version_range = {
|
||||
'addon_min_version': '0',
|
||||
'addon_max_version': '*',
|
||||
'applications': [{
|
||||
'name': amo.FIREFOX.pretty,
|
||||
'id': amo.FIREFOX.id,
|
||||
'min_version': '0',
|
||||
'max_version': '*',
|
||||
'guid': amo.FIREFOX.guid
|
||||
}]
|
||||
}
|
||||
assert result['version_ranges'] == [version_range]
|
||||
|
||||
def test_multiple_ranges(self):
|
||||
override = CompatOverride.objects.create(
|
||||
name='override with multiple ranges', guid='foo@baa')
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=override, app=amo.FIREFOX.id, min_version='23.4',
|
||||
max_version='56.7.*')
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=override, app=amo.ANDROID.id, min_app_version='1.35',
|
||||
max_app_version='90.*')
|
||||
result = self.serialize(override)
|
||||
|
||||
assert ['addon_guid', 'addon_id', 'name', 'version_ranges'] == sorted(
|
||||
result.keys())
|
||||
assert result['addon_guid'] == 'foo@baa'
|
||||
assert result['addon_id'] is None
|
||||
assert result['name'] == 'override with multiple ranges'
|
||||
assert len(result['version_ranges']) == 2
|
||||
version_range_firefox = {
|
||||
'addon_min_version': '23.4',
|
||||
'addon_max_version': '56.7.*',
|
||||
'applications': [{
|
||||
'name': amo.FIREFOX.pretty,
|
||||
'id': amo.FIREFOX.id,
|
||||
'min_version': '0',
|
||||
'max_version': '*',
|
||||
'guid': amo.FIREFOX.guid
|
||||
}]
|
||||
}
|
||||
assert version_range_firefox in result['version_ranges']
|
||||
version_range_android = {
|
||||
'addon_min_version': '0',
|
||||
'addon_max_version': '*',
|
||||
'applications': [{
|
||||
'name': amo.ANDROID.pretty,
|
||||
'id': amo.ANDROID.id,
|
||||
'min_version': '1.35',
|
||||
'max_version': '90.*',
|
||||
'guid': amo.ANDROID.guid
|
||||
}]
|
||||
}
|
||||
assert version_range_android in result['version_ranges']
|
||||
|
||||
def test_collapsed_ranges(self):
|
||||
"""Collapsed ranges are where there is a single version range of
|
||||
affected addons, but multiple applications affected."""
|
||||
override = CompatOverride.objects.create(
|
||||
name='override with single version range', guid='foo@baa')
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=override, app=amo.FIREFOX.id,
|
||||
min_version='23.4', max_version='56.7.*')
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=override, app=amo.ANDROID.id,
|
||||
min_version='23.4', max_version='56.7.*',
|
||||
min_app_version='1.35', max_app_version='90.*')
|
||||
result = self.serialize(override)
|
||||
|
||||
assert ['addon_guid', 'addon_id', 'name', 'version_ranges'] == sorted(
|
||||
result.keys())
|
||||
assert result['addon_guid'] == 'foo@baa'
|
||||
assert result['addon_id'] is None
|
||||
assert result['name'] == 'override with single version range'
|
||||
assert len(result['version_ranges']) == 1
|
||||
assert result['version_ranges'][0]['addon_min_version'] == '23.4'
|
||||
assert result['version_ranges'][0]['addon_max_version'] == '56.7.*'
|
||||
applications = result['version_ranges'][0]['applications']
|
||||
assert len(applications) == 2
|
||||
application_firefox = {
|
||||
'name': amo.FIREFOX.pretty,
|
||||
'id': amo.FIREFOX.id,
|
||||
'min_version': '0',
|
||||
'max_version': '*',
|
||||
'guid': amo.FIREFOX.guid
|
||||
}
|
||||
assert application_firefox in applications
|
||||
application_android = {
|
||||
'name': amo.ANDROID.pretty,
|
||||
'id': amo.ANDROID.id,
|
||||
'min_version': '1.35',
|
||||
'max_version': '90.*',
|
||||
'guid': amo.ANDROID.guid
|
||||
}
|
||||
assert application_android in applications
|
||||
|
|
|
@ -10,8 +10,7 @@ from django.db import connection
|
|||
from services import update
|
||||
|
||||
from olympia import amo
|
||||
from olympia.addons.models import (
|
||||
Addon, CompatOverride, CompatOverrideRange, IncompatibleVersions)
|
||||
from olympia.addons.models import Addon
|
||||
from olympia.amo.tests import TestCase
|
||||
from olympia.applications.models import AppVersion
|
||||
from olympia.files.models import File
|
||||
|
@ -373,16 +372,6 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
|
|||
'8.0-ignore': self.ver_1_3,
|
||||
}
|
||||
|
||||
def create_override(self, **kw):
|
||||
co = CompatOverride.objects.create(
|
||||
name='test', guid=self.addon.guid, addon=self.addon
|
||||
)
|
||||
default = dict(compat=co, app=self.app.id, min_version='0',
|
||||
max_version='*', min_app_version='0',
|
||||
max_app_version='*')
|
||||
default.update(kw)
|
||||
CompatOverrideRange.objects.create(**default)
|
||||
|
||||
def update_files(self, **kw):
|
||||
for version in self.addon.versions.all():
|
||||
for file in version.files.all():
|
||||
|
@ -460,29 +449,6 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
|
|||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_extension_compat_override(self):
|
||||
# Tests simple add-on (non-binary-components, non-strict) with a compat
|
||||
# override.
|
||||
self.create_override(min_version='1.3', max_version='1.3')
|
||||
self.expected.update({
|
||||
'6.0-normal': self.ver_1_2,
|
||||
'7.0-normal': self.ver_1_2,
|
||||
'8.0-normal': self.ver_1_2,
|
||||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_binary_component_compat_override(self):
|
||||
# Tests simple add-on (non-binary-components, non-strict) with a compat
|
||||
# override.
|
||||
self.update_files(binary_components=True)
|
||||
self.create_override(min_version='1.3', max_version='1.3')
|
||||
self.expected.update({
|
||||
'6.0-normal': self.ver_1_2,
|
||||
'7.0-normal': self.ver_1_2,
|
||||
'8.0-normal': None,
|
||||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_strict_opt_in(self):
|
||||
# Tests add-on with opt-in strict compatibility
|
||||
self.update_files(strict_compatibility=True)
|
||||
|
@ -491,49 +457,6 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
|
|||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_compat_override_max_addon_wildcard(self):
|
||||
# Tests simple add-on (non-binary-components, non-strict) with a compat
|
||||
# override that contains a max wildcard.
|
||||
self.create_override(min_version='1.2', max_version='1.3',
|
||||
min_app_version='5.0', max_app_version='6.*')
|
||||
self.expected.update({
|
||||
'5.0-normal': self.ver_1_1,
|
||||
'6.0-normal': self.ver_1_1,
|
||||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_compat_override_max_app_wildcard(self):
|
||||
# Tests simple add-on (non-binary-components, non-strict) with a compat
|
||||
# override that contains a min/max wildcard for the app.
|
||||
|
||||
self.create_override(min_version='1.2', max_version='1.3')
|
||||
self.expected.update({
|
||||
'5.0-normal': self.ver_1_1,
|
||||
'6.0-normal': self.ver_1_1,
|
||||
'7.0-normal': self.ver_1_1,
|
||||
'8.0-normal': self.ver_1_1,
|
||||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_compat_override_both_wildcards(self):
|
||||
# Tests simple add-on (non-binary-components, non-strict) with a compat
|
||||
# override that contains a wildcard for both addon version and app
|
||||
# version.
|
||||
|
||||
self.create_override(min_app_version='7.0', max_app_version='*')
|
||||
self.expected.update({
|
||||
'7.0-normal': None,
|
||||
'8.0-normal': None,
|
||||
})
|
||||
self.check(self.expected)
|
||||
|
||||
def test_compat_override_invalid_version(self):
|
||||
# Tests compat override range where version doesn't match our
|
||||
# versioning scheme. This results in no versions being written to the
|
||||
# incompatible_versions table.
|
||||
self.create_override(min_version='ver1', max_version='ver2')
|
||||
assert IncompatibleVersions.objects.all().count() == 0
|
||||
|
||||
def test_min_max_version(self):
|
||||
# Tests the minimum requirement of the app maxVersion.
|
||||
av = self.addon.current_version.apps.all()[0]
|
||||
|
|
|
@ -16,9 +16,7 @@ from waffle import switch_is_active
|
|||
from waffle.testutils import override_switch
|
||||
|
||||
from olympia import amo
|
||||
from olympia.addons.models import (
|
||||
Addon, AddonUser, Category, CompatOverride,
|
||||
CompatOverrideRange, ReplacementAddon)
|
||||
from olympia.addons.models import Addon, AddonUser, Category, ReplacementAddon
|
||||
from olympia.addons.utils import generate_addon_guid
|
||||
from olympia.addons.views import (
|
||||
DEFAULT_FIND_REPLACEMENT_PATH, FIND_REPLACEMENT_SRC,
|
||||
|
@ -2557,116 +2555,19 @@ class TestReplacementAddonView(TestCase):
|
|||
class TestCompatOverrideView(TestCase):
|
||||
"""This view is used by Firefox directly and queried a lot.
|
||||
|
||||
That's why there are performance sensitive tests.
|
||||
But now we don't have any CompatOverrides we just return an empty response.
|
||||
"""
|
||||
|
||||
client_class = APITestClient
|
||||
|
||||
def setUp(self):
|
||||
self.addon = addon_factory(guid='extrabad@thing')
|
||||
self.override_addon = CompatOverride.objects.create(
|
||||
name='override with addon', guid=self.addon.guid, addon=self.addon)
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=self.override_addon, app=amo.FIREFOX.id)
|
||||
self.override_without = CompatOverride.objects.create(
|
||||
name='override no addon', guid='bad@thing')
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=self.override_without, app=amo.FIREFOX.id)
|
||||
|
||||
def test_single_guid(self):
|
||||
def test_response(self):
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
data={'guid': u'extrabad@thing'})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(force_text(response.content))
|
||||
assert len(data['results']) == 1
|
||||
result = data['results'][0]
|
||||
assert result['addon_guid'] == 'extrabad@thing'
|
||||
assert result['addon_id'] == self.addon.id
|
||||
assert result['name'] == 'override with addon'
|
||||
|
||||
def test_multiple_guid(self):
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
reverse_ns('addon-compat-override', api_version='v3'),
|
||||
data={'guid': u'extrabad@thing,bad@thing'})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(force_text(response.content))
|
||||
results = data['results']
|
||||
assert len(results) == 2
|
||||
|
||||
assert results[0]['addon_guid'] == 'bad@thing'
|
||||
assert results[0]['addon_id'] is None
|
||||
assert results[0]['name'] == 'override no addon'
|
||||
assert results[1]['addon_guid'] == 'extrabad@thing'
|
||||
assert results[1]['addon_id'] == self.addon.id
|
||||
assert results[1]['name'] == 'override with addon'
|
||||
|
||||
# Throw in some random invalid guids too that will be ignored.
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
data={'guid': (
|
||||
u'extrabad@thing,invalid@guid,notevenaguid$,bad@thing')})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(force_text(response.content))
|
||||
results = data['results']
|
||||
assert len(results) == 2
|
||||
assert results[0]['addon_guid'] == 'bad@thing'
|
||||
assert results[1]['addon_guid'] == 'extrabad@thing'
|
||||
|
||||
def test_no_guid_param(self):
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
data={'guid': u'invalid@thing'})
|
||||
# Searching for non-matching guids, it should be an empty 200 response.
|
||||
assert response.status_code == 200
|
||||
assert len(json.loads(force_text(response.content))['results']) == 0
|
||||
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'), data={'guid': ''})
|
||||
# Empty query is a 400 because a guid is required for overrides.
|
||||
assert response.status_code == 400
|
||||
assert b'Empty, or no, guid parameter provided.' in response.content
|
||||
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'))
|
||||
# And no guid param should be a 400 too
|
||||
assert response.status_code == 400
|
||||
assert b'Empty, or no, guid parameter provided.' in response.content
|
||||
|
||||
def test_performance_no_matching_guid(self):
|
||||
# There is at least one query from the paginator, counting all objects
|
||||
# We do not query on `compat_override` though if the count is 0.
|
||||
with self.assertNumQueries(1):
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
data={'guid': u'unknownguid'})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(force_text(response.content))
|
||||
assert len(data['results']) == 0
|
||||
|
||||
def test_performance_matches_one_guid(self):
|
||||
# 1. Query is querying compat_override
|
||||
# 2. Query is adding CompatOverrideRange via the transformer
|
||||
with self.assertNumQueries(2):
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
data={'guid': u'extrabad@thing'})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(force_text(response.content))
|
||||
assert len(data['results']) == 1
|
||||
|
||||
def test_performance_matches_multiple_guid(self):
|
||||
# 1. Query is querying compat_override
|
||||
# 2. Query is adding CompatOverrideRange via the transformer
|
||||
with self.assertNumQueries(2):
|
||||
response = self.client.get(
|
||||
reverse_ns('addon-compat-override'),
|
||||
data={'guid': (
|
||||
u'extrabad@thing,invalid@guid,notevenaguid$,'
|
||||
u'bad@thing')})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(force_text(response.content))
|
||||
assert len(data['results']) == 2
|
||||
assert len(results) == 0
|
||||
|
||||
|
||||
class TestAddonRecommendationView(ESTestCase):
|
||||
|
|
|
@ -15,6 +15,7 @@ from rest_framework.generics import GenericAPIView, ListAPIView
|
|||
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.settings import api_settings
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
import olympia.core.logger
|
||||
|
@ -30,8 +31,7 @@ from olympia.api.permissions import (
|
|||
from olympia.constants.categories import CATEGORIES_BY_ID
|
||||
from olympia.search.filters import (
|
||||
AddonAppQueryParam, AddonAppVersionQueryParam, AddonAuthorQueryParam,
|
||||
AddonCategoryQueryParam, AddonGuidQueryParam, AddonTypeQueryParam,
|
||||
AutoCompleteSortFilter,
|
||||
AddonCategoryQueryParam, AddonTypeQueryParam, AutoCompleteSortFilter,
|
||||
ReviewedContentFilter, SearchParameterFilter, SearchQueryFilter,
|
||||
SortingFilter)
|
||||
from olympia.translations.query import order_by_translation
|
||||
|
@ -39,10 +39,10 @@ from olympia.versions.models import Version
|
|||
|
||||
from .decorators import addon_view_factory
|
||||
from .indexers import AddonIndexer
|
||||
from .models import Addon, CompatOverride, ReplacementAddon
|
||||
from .models import Addon, ReplacementAddon
|
||||
from .serializers import (
|
||||
AddonEulaPolicySerializer,
|
||||
AddonSerializer, AddonSerializerWithUnlistedData, CompatOverrideSerializer,
|
||||
AddonSerializer, AddonSerializerWithUnlistedData,
|
||||
ESAddonAutoCompleteSerializer, ESAddonSerializer, LanguageToolsSerializer,
|
||||
ReplacementAddonSerializer, StaticCategorySerializer, VersionSerializer)
|
||||
from .utils import (
|
||||
|
@ -677,43 +677,17 @@ class ReplacementAddonView(ListAPIView):
|
|||
serializer_class = ReplacementAddonSerializer
|
||||
|
||||
|
||||
class CompatOverrideView(ListAPIView):
|
||||
"""This view is used by Firefox so it's performance-critical.
|
||||
|
||||
Every firefox client requests the list of overrides approx. once per day.
|
||||
Firefox requests the overrides via a list of GUIDs which makes caching
|
||||
hard because the variation of possible GUID combinations prevent us to
|
||||
simply add some dumb-caching and requires us to resolve cache-misses.
|
||||
class CompatOverrideView(APIView):
|
||||
"""This view is used by Firefox but we don't have any more overrides so we
|
||||
just return an empty response. This api is v3 only.
|
||||
"""
|
||||
|
||||
queryset = CompatOverride.objects.all()
|
||||
serializer_class = CompatOverrideSerializer
|
||||
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
"""The API is read-only so we can turn off atomic requests."""
|
||||
return non_atomic_requests(
|
||||
super(CompatOverrideView, cls).as_view(**initkwargs))
|
||||
return non_atomic_requests(super().as_view(**initkwargs))
|
||||
|
||||
def get_guids(self):
|
||||
# Use the same Filter we use for AddonSearchView for consistency.
|
||||
guid_filter = AddonGuidQueryParam(self.request)
|
||||
return guid_filter.get_value()
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
guids = self.get_guids()
|
||||
if not guids:
|
||||
raise exceptions.ParseError(
|
||||
'Empty, or no, guid parameter provided.')
|
||||
# Evaluate the queryset and cast it into a list.
|
||||
# This will force Django to simply use len(queryset) instead of
|
||||
# calling .count() on it and avoids an additional COUNT query.
|
||||
# The amount of GUIDs we should get in real-life won't be paginated
|
||||
# most of the time so it's safe to simply evaluate the query.
|
||||
# The advantage here is that we are saving ourselves a `COUNT` query
|
||||
# and these are expensive.
|
||||
return list(queryset.filter(guid__in=guids).transform(
|
||||
CompatOverride.transformer).order_by('-pk'))
|
||||
def get(self, request, format=None):
|
||||
return Response({'results': []})
|
||||
|
||||
|
||||
class AddonRecommendationView(AddonSearchView):
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from django.conf.urls import include, url
|
||||
|
||||
from olympia.addons.api_urls import addons_v3, addons_v4
|
||||
from olympia.ratings.api_urls import ratings_v3, ratings_v4
|
||||
|
||||
|
||||
v3_api_urls = [
|
||||
url(r'^abuse/', include('olympia.abuse.urls')),
|
||||
url(r'^accounts/', include('olympia.accounts.urls')),
|
||||
url(r'^addons/', include('olympia.addons.api_urls')),
|
||||
url(r'^addons/', include(addons_v3)),
|
||||
url(r'^', include('olympia.discovery.api_urls')),
|
||||
url(r'^reviews/', include(ratings_v3.urls)),
|
||||
url(r'^reviewers/', include('olympia.reviewers.api_urls')),
|
||||
|
@ -18,7 +19,7 @@ v4_api_urls = [
|
|||
url(r'^abuse/', include('olympia.abuse.urls')),
|
||||
url(r'^accounts/', include('olympia.accounts.urls')),
|
||||
url(r'^activity/', include('olympia.activity.urls')),
|
||||
url(r'^addons/', include('olympia.addons.api_urls')),
|
||||
url(r'^addons/', include(addons_v4)),
|
||||
url(r'^', include('olympia.discovery.api_urls')),
|
||||
url(r'^ratings/', include(ratings_v4.urls)),
|
||||
url(r'^reviewers/', include('olympia.reviewers.api_urls')),
|
||||
|
|
|
@ -1125,8 +1125,6 @@ CELERY_TASK_ROUTES = {
|
|||
# Addons
|
||||
'olympia.addons.tasks.delete_preview_files': {'queue': 'addons'},
|
||||
'olympia.versions.tasks.delete_preview_files': {'queue': 'addons'},
|
||||
'olympia.addons.tasks.update_incompatible_appversions': {
|
||||
'queue': 'addons'},
|
||||
'olympia.addons.tasks.version_changed': {'queue': 'addons'},
|
||||
|
||||
# API
|
||||
|
|
|
@ -1,31 +1,2 @@
|
|||
from addons.models import Addon, CompatOverride, CompatOverrideRange
|
||||
|
||||
from olympia import amo
|
||||
|
||||
|
||||
def run():
|
||||
addons = (
|
||||
Addon.objects
|
||||
.filter(type=amo.ADDON_EXTENSION, appsupport__app=amo.FIREFOX.id,
|
||||
_current_version__files__jetpack_version__isnull=False)
|
||||
.exclude(_current_version__files__jetpack_version='1.14'))
|
||||
|
||||
# Fix invalid compat ranges from last migration
|
||||
(CompatOverrideRange.objects.filter(
|
||||
compat__addon__in=addons, type=1, app_id=amo.FIREFOX.id,
|
||||
min_app_version='0', max_app_version='21.*', min_version='0')
|
||||
.delete())
|
||||
|
||||
count = 0
|
||||
for addon in addons:
|
||||
co, created = CompatOverride.objects.get_or_create(addon=addon,
|
||||
guid=addon.guid,
|
||||
name=addon.name)
|
||||
CompatOverrideRange.objects.create(
|
||||
compat=co, type=1, app_id=amo.FIREFOX.id,
|
||||
min_app_version='21.*', max_app_version='*',
|
||||
min_version='0', max_version=addon.current_version.version)
|
||||
|
||||
count += 1
|
||||
|
||||
print('Overrode compatibility for %d SDK add-ons.' % count)
|
||||
pass
|
||||
|
|
|
@ -691,21 +691,6 @@ def inherit_nomination(sender, instance, **kw):
|
|||
instance.inherit_nomination()
|
||||
|
||||
|
||||
def update_incompatible_versions(sender, instance, **kw):
|
||||
"""
|
||||
When a new version is added or deleted, send to task to update if it
|
||||
matches any compat overrides.
|
||||
"""
|
||||
try:
|
||||
if not instance.addon.type == amo.ADDON_EXTENSION:
|
||||
return
|
||||
except ObjectDoesNotExist:
|
||||
return
|
||||
|
||||
from olympia.addons import tasks
|
||||
tasks.update_incompatible_appversions.delay([instance.id])
|
||||
|
||||
|
||||
def cleanup_version(sender, instance, **kw):
|
||||
"""On delete of the version object call the file delete and signals."""
|
||||
if kw.get('raw'):
|
||||
|
@ -737,17 +722,11 @@ models.signals.post_save.connect(
|
|||
models.signals.post_save.connect(
|
||||
inherit_nomination, sender=Version,
|
||||
dispatch_uid='version_inherit_nomination')
|
||||
models.signals.post_save.connect(
|
||||
update_incompatible_versions, sender=Version,
|
||||
dispatch_uid='version_update_incompat')
|
||||
|
||||
models.signals.pre_delete.connect(
|
||||
cleanup_version, sender=Version, dispatch_uid='cleanup_version')
|
||||
models.signals.post_delete.connect(
|
||||
update_status, sender=Version, dispatch_uid='version_update_status')
|
||||
models.signals.post_delete.connect(
|
||||
update_incompatible_versions, sender=Version,
|
||||
dispatch_uid='version_update_incompat')
|
||||
|
||||
|
||||
class LicenseManager(ManagerBase):
|
||||
|
|
|
@ -17,8 +17,7 @@ from waffle.testutils import override_switch
|
|||
|
||||
from olympia import amo, core
|
||||
from olympia.activity.models import ActivityLog
|
||||
from olympia.addons.models import (
|
||||
Addon, AddonReviewerFlags, CompatOverride, CompatOverrideRange)
|
||||
from olympia.addons.models import Addon, AddonReviewerFlags
|
||||
from olympia.amo.tests import (
|
||||
TestCase, addon_factory, user_factory, version_factory)
|
||||
from olympia.amo.tests.test_models import BasePreviewMixin
|
||||
|
@ -424,26 +423,6 @@ class TestVersion(TestCase):
|
|||
# An app that can't do d2c should also be False.
|
||||
assert not version.is_compatible_app(amo.UNKNOWN_APP)
|
||||
|
||||
def test_compat_override_app_versions(self):
|
||||
addon = Addon.objects.get(id=3615)
|
||||
version = version_factory(addon=addon)
|
||||
co = CompatOverride.objects.create(addon=addon)
|
||||
CompatOverrideRange.objects.create(compat=co, app=1, min_version='0',
|
||||
max_version=version.version,
|
||||
min_app_version='10.0a1',
|
||||
max_app_version='10.*')
|
||||
assert version.compat_override_app_versions() == [('10.0a1', '10.*')]
|
||||
|
||||
def test_compat_override_app_versions_wildcard(self):
|
||||
addon = Addon.objects.get(id=3615)
|
||||
version = version_factory(addon=addon)
|
||||
co = CompatOverride.objects.create(addon=addon)
|
||||
CompatOverrideRange.objects.create(compat=co, app=1, min_version='0',
|
||||
max_version='*',
|
||||
min_app_version='10.0a1',
|
||||
max_app_version='10.*')
|
||||
assert version.compat_override_app_versions() == [('10.0a1', '10.*')]
|
||||
|
||||
def test_get_url_path(self):
|
||||
assert self.version.get_url_path() == (
|
||||
'/en-US/firefox/addon/a3615/versions/')
|
||||
|
|
Загрузка…
Ссылка в новой задаче