promoted addon search api filter (#15324)
* promoted addon search api filter * test all the groups; limit param values to enabled groups
This commit is contained in:
Родитель
8d71c53b1b
Коммит
b1fa006974
|
@ -33,6 +33,7 @@ This endpoint allows you to search through public add-ons.
|
|||
: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.
|
||||
:query string platform: Filter by :ref:`add-on platform <addon-detail-platform>` availability.
|
||||
:query string promoted: Filter to add-ons in a specific :ref:`promoted category <addon-detail-promoted-category>`. Can be combined with `app`.
|
||||
:query boolean recommended: Filter to only add-ons recommended by Mozilla. Only ``recommended=true`` is supported.
|
||||
:query string tag: Filter by exact tag name. Multiple tag names can be specified, separated by comma(s), in which case add-ons containing *all* specified tags are returned.
|
||||
:query string type: Filter by :ref:`add-on type <addon-detail-type>`. Multiple types can be specified, separated by comma(s), in which case add-ons that are any of the matching types are returned.
|
||||
|
@ -274,7 +275,7 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
|
|||
============== ==========================================================
|
||||
Value Description
|
||||
============== ==========================================================
|
||||
line Line category
|
||||
line "By Firefox" category
|
||||
recommended Recommended category
|
||||
sponsored Sponsored category
|
||||
spotlight Spotlight category
|
||||
|
|
|
@ -373,6 +373,7 @@ v4 API changelog
|
|||
* 2020-03-19: added /blocklist/block endpoint to expose add-on blocks https://github.com/mozilla/addons-server/issues/13706.
|
||||
* 2020-03-26: added ``addon_name`` to blocklist/block api https://github.com/mozilla/addons-server/issues/13757
|
||||
* 2020-08-13: added ``applications`` internal API to create new application versions https://github.com/mozilla/addons-server/issues/14649
|
||||
* 2020-09-03: added ``promoted`` filter to addons search api https://github.com/mozilla/addons-server/issues/15272.
|
||||
|
||||
.. _`#11380`: https://github.com/mozilla/addons-server/issues/11380/
|
||||
.. _`#11379`: https://github.com/mozilla/addons-server/issues/11379/
|
||||
|
|
|
@ -26,7 +26,8 @@ from olympia.amo.tests import (
|
|||
from olympia.amo.urlresolvers import get_outgoing_url, reverse
|
||||
from olympia.bandwagon.models import CollectionAddon
|
||||
from olympia.constants.categories import CATEGORIES, CATEGORIES_BY_ID
|
||||
from olympia.constants.promoted import RECOMMENDED
|
||||
from olympia.constants.promoted import (
|
||||
LINE, SPOTLIGHT, STRATEGIC, RECOMMENDED, VERIFIED_ONE, VERIFIED_TWO)
|
||||
from olympia.discovery.models import DiscoveryItem
|
||||
from olympia.users.models import UserProfile
|
||||
from olympia.versions.models import ApplicationsVersions, AppVersion
|
||||
|
@ -1236,6 +1237,62 @@ class TestAddonSearchView(ESTestCase):
|
|||
assert len(data['results']) == 1
|
||||
assert data['results'][0]['id'] == addon.pk
|
||||
|
||||
def test_filter_by_promoted(self):
|
||||
av_min, _ = AppVersion.objects.get_or_create(
|
||||
application=amo.ANDROID.id, version='59.0.0')
|
||||
av_max, _ = AppVersion.objects.get_or_create(
|
||||
application=amo.ANDROID.id, version='60.0.0')
|
||||
|
||||
addon = addon_factory(name='Recomménded Addôn')
|
||||
ApplicationsVersions.objects.get_or_create(
|
||||
application=amo.ANDROID.id, version=addon.current_version,
|
||||
min=av_min, max=av_max)
|
||||
self.make_addon_promoted(addon, RECOMMENDED, approve_version=True)
|
||||
|
||||
addon2 = addon_factory(name='Fírefox Addôn')
|
||||
ApplicationsVersions.objects.get_or_create(
|
||||
application=amo.ANDROID.id, version=addon2.current_version,
|
||||
min=av_min, max=av_max)
|
||||
self.make_addon_promoted(addon2, RECOMMENDED, approve_version=True)
|
||||
addon2.promotedaddon.update(application_id=amo.FIREFOX.id)
|
||||
|
||||
addon3 = addon_factory(slug='other-addon', name=u'Other Addôn')
|
||||
ApplicationsVersions.objects.get_or_create(
|
||||
application=amo.ANDROID.id, version=addon3.current_version,
|
||||
min=av_min, max=av_max)
|
||||
self.reindex(Addon)
|
||||
|
||||
data = self.perform_search(
|
||||
self.url, {'promoted': 'recommended'})
|
||||
assert data['count'] == 2
|
||||
assert len(data['results']) == 2
|
||||
assert {res['id'] for res in data['results']} == {addon.pk, addon2.pk}
|
||||
|
||||
# And with app filtering too
|
||||
data = self.perform_search(
|
||||
self.url, {'promoted': 'recommended', 'app': 'firefox'})
|
||||
assert data['count'] == 2
|
||||
assert len(data['results']) == 2
|
||||
assert {res['id'] for res in data['results']} == {addon.pk, addon2.pk}
|
||||
|
||||
# That will filter out for a different app
|
||||
data = self.perform_search(
|
||||
self.url, {'promoted': 'recommended', 'app': 'android'})
|
||||
assert data['count'] == 1
|
||||
assert len(data['results']) == 1
|
||||
assert data['results'][0]['id'] == addon.pk
|
||||
# addon2 was for Firefox only
|
||||
|
||||
# test with other other promotions
|
||||
for promo in (VERIFIED_ONE, VERIFIED_TWO, LINE, SPOTLIGHT, STRATEGIC):
|
||||
self.make_addon_promoted(addon, promo, approve_version=True)
|
||||
self.reindex(Addon)
|
||||
data = self.perform_search(
|
||||
self.url, {'promoted': promo.api_name, 'app': 'firefox'})
|
||||
assert data['count'] == 1
|
||||
assert len(data['results']) == 1
|
||||
assert data['results'][0]['id'] == addon.pk
|
||||
|
||||
def test_filter_by_platform(self):
|
||||
# First add-on is available for all platforms.
|
||||
addon = addon_factory(slug='my-addon', name=u'My Addôn',
|
||||
|
|
|
@ -114,3 +114,4 @@ PROMOTED_GROUPS = [
|
|||
PRE_REVIEW_GROUPS = [group for group in PROMOTED_GROUPS if group.pre_review]
|
||||
|
||||
PROMOTED_GROUPS_BY_ID = {p.id: p for p in PROMOTED_GROUPS}
|
||||
ENABLED_PROMOTED_GROUPS_BY_ID = {p.id: p for p in PROMOTED_GROUPS if p}
|
||||
|
|
|
@ -14,6 +14,7 @@ from waffle import switch_is_active
|
|||
from olympia import amo
|
||||
from olympia.api.utils import is_gate_active
|
||||
from olympia.constants.categories import CATEGORIES, CATEGORIES_BY_ID
|
||||
from olympia.constants.promoted import ENABLED_PROMOTED_GROUPS_BY_ID
|
||||
from olympia.discovery.models import DiscoveryItem
|
||||
from olympia.versions.compare import version_int
|
||||
|
||||
|
@ -335,6 +336,32 @@ class AddonRecommendedQueryParam(AddonQueryParam):
|
|||
es_field = 'is_recommended'
|
||||
|
||||
|
||||
class AddonPromotedQueryParam(AddonQueryParam):
|
||||
query_param = 'promoted'
|
||||
reverse_dict = {
|
||||
group.api_name: id_
|
||||
for (id_, group) in ENABLED_PROMOTED_GROUPS_BY_ID.items()}
|
||||
valid_values = ENABLED_PROMOTED_GROUPS_BY_ID.keys()
|
||||
|
||||
def get_app(self):
|
||||
return (
|
||||
AddonAppQueryParam(self.request).get_value()
|
||||
if AddonAppQueryParam.query_param in self.request.GET
|
||||
else None)
|
||||
|
||||
def get_es_query(self):
|
||||
query = [Q(
|
||||
self.operator,
|
||||
**{'promoted.group_id': self.get_value()})]
|
||||
|
||||
if app := self.get_app():
|
||||
query.append(
|
||||
Q(self.operator, **{'promoted.application_id': app}) |
|
||||
~Q('exists', field='promoted.application_id'))
|
||||
|
||||
return query
|
||||
|
||||
|
||||
class AddonColorQueryParam(AddonQueryParam):
|
||||
query_param = 'color'
|
||||
|
||||
|
@ -781,6 +808,7 @@ class SearchParameterFilter(BaseFilterBackend):
|
|||
AddonFeaturedQueryParam,
|
||||
AddonGuidQueryParam,
|
||||
AddonPlatformQueryParam,
|
||||
AddonPromotedQueryParam,
|
||||
AddonRecommendedQueryParam,
|
||||
AddonTagQueryParam,
|
||||
AddonTypeQueryParam,
|
||||
|
|
|
@ -13,6 +13,7 @@ from rest_framework import serializers
|
|||
from olympia import amo
|
||||
from olympia.amo.tests import TestCase
|
||||
from olympia.constants.categories import CATEGORIES
|
||||
from olympia.constants.promoted import ENABLED_PROMOTED_GROUPS_BY_ID
|
||||
from olympia.search.filters import (
|
||||
ReviewedContentFilter, SearchParameterFilter, SearchQueryFilter,
|
||||
SortingFilter)
|
||||
|
@ -907,6 +908,26 @@ class TestSearchParameterFilter(FilterTestsBase):
|
|||
self._filter(data={'recommended': 'false'})
|
||||
assert context.exception.detail == ['Invalid "recommended" parameter.']
|
||||
|
||||
def test_search_by_promoted(self):
|
||||
with self.assertRaises(serializers.ValidationError) as context:
|
||||
self._filter(data={'promoted': 'foo'})
|
||||
assert context.exception.detail == ['Invalid "promoted" parameter.']
|
||||
|
||||
for promo in ENABLED_PROMOTED_GROUPS_BY_ID.values():
|
||||
qs = self._filter(data={'promoted': promo.api_name})
|
||||
filter_ = qs['query']['bool']['filter']
|
||||
assert [{'term': {'promoted.group_id': promo.id}}] == filter_
|
||||
|
||||
qs = self._filter(
|
||||
data={'promoted': promo.api_name, 'app': 'firefox'})
|
||||
filter_ = qs['query']['bool']['filter']
|
||||
assert {'term': {'promoted.group_id': promo.id}} in filter_
|
||||
app_filter = filter_[-1]['bool']['should']
|
||||
assert {'term': {'promoted.application_id': amo.FIREFOX.id}} in (
|
||||
app_filter)
|
||||
assert {'bool': {'must_not': [{'exists': {
|
||||
'field': 'promoted.application_id'}}]}} in app_filter
|
||||
|
||||
def test_search_by_color(self):
|
||||
qs = self._filter(data={'color': 'ff0000'})
|
||||
filter_ = qs['query']['bool']['filter']
|
||||
|
|
Загрузка…
Ссылка в новой задаче