Merge pull request #3128 from diox/api-expose-authors
Expose author information in add-on search and detail API
This commit is contained in:
Коммит
1c273a1dc5
|
@ -68,6 +68,10 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
|
|||
.. _addon-detail-object:
|
||||
|
||||
:>json int id: The add-on id on AMO.
|
||||
:>json array authors: Array holding information about the authors for this add-on.
|
||||
:>json int authors[].id: The id for an author.
|
||||
:>json string authors[].name: The name for an author.
|
||||
:>json string authors[].url: The link to the profile page for an author.
|
||||
:>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.
|
||||
|
|
|
@ -11,7 +11,7 @@ norecursedirs =
|
|||
DJANGO_SETTINGS_MODULE = settings_test
|
||||
|
||||
[flake8]
|
||||
ignore = F999
|
||||
ignore = F999,F405
|
||||
exclude =
|
||||
services,
|
||||
src/olympia/wsgi.py,
|
||||
|
|
|
@ -36,6 +36,11 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
'app': {'type': 'byte'},
|
||||
'appversion': {'properties': {app.id: appver
|
||||
for app in amo.APP_USAGE}},
|
||||
# FIXME: See issue #3120, the 'authors' property is for
|
||||
# backwards-compatibility and all code should be switched
|
||||
# to use 'listed_authors.name' instead. We needed a reindex
|
||||
# first though, which is why the 2 are present at the
|
||||
# moment.
|
||||
'authors': {'type': 'string'},
|
||||
'average_daily_users': {'type': 'long'},
|
||||
'bayesian_rating': {'type': 'double'},
|
||||
|
@ -74,6 +79,14 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
'is_disabled': {'type': 'boolean'},
|
||||
'is_listed': {'type': 'boolean'},
|
||||
'last_updated': {'type': 'date'},
|
||||
'listed_authors': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'long', 'index': 'no'},
|
||||
'name': {'type': 'string'},
|
||||
'username': {'type': 'string', 'index': 'no'},
|
||||
},
|
||||
},
|
||||
'modified': {'type': 'date', 'index': 'no'},
|
||||
# Adding word-delimiter to split on camelcase and
|
||||
# punctuation.
|
||||
|
@ -176,6 +189,11 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
'min': min_, 'min_human': min_human,
|
||||
'max': max_, 'max_human': max_human,
|
||||
}
|
||||
# FIXME: See issue #3120, the 'authors' property is for
|
||||
# backwards-compatibility and all code should be switched
|
||||
# to use 'listed_authors.name' instead. We needed a reindex
|
||||
# first though, which is why the 2 are present at the
|
||||
# moment.
|
||||
data['authors'] = [a.name for a in obj.listed_authors]
|
||||
# Quadruple the boost if the add-on is public.
|
||||
if obj.status == amo.STATUS_PUBLIC and 'boost' in data:
|
||||
|
@ -203,6 +221,10 @@ class AddonIndexer(BaseSearchIndexer):
|
|||
obj.current_version.supported_platforms]
|
||||
else:
|
||||
data['has_version'] = None
|
||||
data['listed_authors'] = [
|
||||
{'name': a.name, 'id': a.id, 'username': a.username}
|
||||
for a in obj.listed_authors
|
||||
]
|
||||
|
||||
# We can use all_previews because the indexing code goes through the
|
||||
# transformer that sets it.
|
||||
|
|
|
@ -10,6 +10,7 @@ from olympia.api.serializers import BaseESSerializer
|
|||
from olympia.applications.models import AppVersion
|
||||
from olympia.constants.applications import APPS_ALL
|
||||
from olympia.files.models import File
|
||||
from olympia.users.models import UserProfile
|
||||
from olympia.versions.models import ApplicationsVersions, Version
|
||||
|
||||
|
||||
|
@ -22,6 +23,17 @@ class AddonFeatureCompatibilitySerializer(serializers.ModelSerializer):
|
|||
fields = ('e10s', )
|
||||
|
||||
|
||||
class AddonAuthorSerializer(serializers.ModelSerializer):
|
||||
url = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ('name', 'url')
|
||||
|
||||
def get_url(self, obj):
|
||||
return absolutify(obj.get_url_path())
|
||||
|
||||
|
||||
class FileSerializer(serializers.ModelSerializer):
|
||||
url = serializers.SerializerMethodField()
|
||||
platform = ReverseChoiceField(choices=amo.PLATFORM_CHOICES_API.items())
|
||||
|
@ -99,6 +111,7 @@ class VersionSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class AddonSerializer(serializers.ModelSerializer):
|
||||
authors = AddonAuthorSerializer(many=True, source='listed_authors')
|
||||
current_version = VersionSerializer()
|
||||
description = TranslationSerializerField()
|
||||
edit_url = serializers.SerializerMethodField()
|
||||
|
@ -118,11 +131,12 @@ class AddonSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('id', 'current_version', 'default_locale', 'description',
|
||||
'edit_url', 'guid', 'homepage', 'icon_url', 'is_listed',
|
||||
'name', 'last_updated', 'previews', 'public_stats',
|
||||
'review_url', 'slug', 'status', 'summary', 'support_email',
|
||||
'support_url', 'tags', 'theme_data', 'type', 'url')
|
||||
fields = ('id', 'authors', 'current_version', 'default_locale',
|
||||
'description', 'edit_url', 'guid', 'homepage', 'icon_url',
|
||||
'is_listed', 'name', 'last_updated', 'previews',
|
||||
'public_stats', 'review_url', 'slug', 'status', 'summary',
|
||||
'support_email', 'support_url', 'tags', 'theme_data', 'type',
|
||||
'url')
|
||||
|
||||
def to_representation(self, obj):
|
||||
data = super(AddonSerializer, self).to_representation(obj)
|
||||
|
@ -249,6 +263,14 @@ class ESAddonSerializer(BaseESSerializer, AddonSerializer):
|
|||
|
||||
obj._current_version.compatible_apps = compatible_apps
|
||||
|
||||
data_authors = data.get('listed_authors', [])
|
||||
obj.listed_authors = [
|
||||
UserProfile(
|
||||
id=data_author['id'], display_name=data_author['name'],
|
||||
username=data_author['username'])
|
||||
for data_author in data_authors
|
||||
]
|
||||
|
||||
# We set obj.all_previews to the raw preview data because
|
||||
# ESPreviewSerializer will handle creating the fake Preview object
|
||||
# for us when its to_representation() method is called.
|
||||
|
|
|
@ -46,8 +46,8 @@ class TestAddonIndexer(TestCase):
|
|||
complex_fields = [
|
||||
'app', 'appversion', 'authors', 'boost', 'category',
|
||||
'current_version', 'description', 'has_theme_rereview',
|
||||
'has_version', 'name', 'name_sort', 'platforms', 'previews',
|
||||
'public_stats', 'summary', 'tags',
|
||||
'has_version', 'listed_authors', 'name', 'name_sort', 'platforms',
|
||||
'previews', 'public_stats', 'summary', 'tags',
|
||||
]
|
||||
|
||||
# Fields that need to be present in the mapping, but might be skipped
|
||||
|
@ -146,6 +146,8 @@ class TestAddonIndexer(TestCase):
|
|||
assert extracted['boost'] == self.addon.average_daily_users ** .2 * 4
|
||||
assert extracted['category'] == [22, 23, 24] # From fixture.
|
||||
assert extracted['has_theme_rereview'] is None
|
||||
assert extracted['listed_authors'] == [
|
||||
{'name': u'55021 التطب', 'id': 55021, 'username': '55021'}]
|
||||
assert extracted['platforms'] == [PLATFORM_ALL.id]
|
||||
assert extracted['tags'] == []
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ from rest_framework.test import APIRequestFactory
|
|||
|
||||
from olympia import amo
|
||||
from olympia.amo.helpers import absolutify
|
||||
from olympia.amo.tests import addon_factory, ESTestCase, TestCase
|
||||
from olympia.amo.tests import addon_factory, ESTestCase, TestCase, user_factory
|
||||
from olympia.amo.urlresolvers import reverse
|
||||
from olympia.addons.indexers import AddonIndexer
|
||||
from olympia.addons.models import Addon, Persona, Preview
|
||||
from olympia.addons.models import Addon, AddonUser, Persona, Preview
|
||||
from olympia.addons.serializers import AddonSerializer, ESAddonSerializer
|
||||
from olympia.addons.utils import generate_addon_guid
|
||||
|
||||
|
@ -39,6 +39,16 @@ class AddonSerializerOutputTestMixin(object):
|
|||
support_url=u'https://support.example.org/support/my-addon/',
|
||||
tags=['some_tag', 'some_other_tag'],
|
||||
)
|
||||
AddonUser.objects.create(user=user_factory(username='hidden_author'),
|
||||
addon=self.addon, listed=False)
|
||||
second_author = user_factory(
|
||||
username='second_author', display_name=u'Secönd Author')
|
||||
first_author = user_factory(
|
||||
username='first_author', display_name=u'First Authôr')
|
||||
AddonUser.objects.create(
|
||||
user=second_author, addon=self.addon, position=2)
|
||||
AddonUser.objects.create(
|
||||
user=first_author, addon=self.addon, position=1)
|
||||
second_preview = Preview.objects.create(
|
||||
addon=self.addon, position=2,
|
||||
caption={'en-US': u'My câption', 'fr': u'Mön tîtré'})
|
||||
|
@ -76,6 +86,15 @@ class AddonSerializerOutputTestMixin(object):
|
|||
assert result['current_version']['url'] == absolutify(
|
||||
version.get_url_path())
|
||||
|
||||
assert result['authors']
|
||||
assert len(result['authors']) == 2
|
||||
assert result['authors'][0] == {
|
||||
'name': first_author.name,
|
||||
'url': absolutify(first_author.get_url_path())}
|
||||
assert result['authors'][1] == {
|
||||
'name': second_author.name,
|
||||
'url': absolutify(second_author.get_url_path())}
|
||||
|
||||
assert result['edit_url'] == absolutify(self.addon.get_dev_url())
|
||||
assert result['default_locale'] == self.addon.default_locale
|
||||
assert result['description'] == {'en-US': self.addon.description}
|
||||
|
|
Загрузка…
Ссылка в новой задаче