Merge pull request #5939 from diox/add-language-tools-api
Add language tools API
This commit is contained in:
Коммит
875c13b3c4
|
@ -359,3 +359,35 @@ This endpoint allows you to fetch an add-on EULA and privacy policy.
|
|||
|
||||
:>json string|object|null eula: The text of the EULA, if present (See :ref:`translated fields <api-overview-translations>`).
|
||||
:>json string|object|null privacy_policy: The text of the Privacy Policy, if present (See :ref:`translated fields <api-overview-translations>`).
|
||||
|
||||
|
||||
--------------
|
||||
Language Tools
|
||||
--------------
|
||||
|
||||
.. _addon-language-tools:
|
||||
|
||||
This endpoint allows you to list all public language tools add-ons available
|
||||
on AMO.
|
||||
|
||||
.. http:get:: /api/v3/addons/language-tools/
|
||||
|
||||
.. note::
|
||||
Because this endpoint is intended to be used to feed a page that
|
||||
displays all available language tools in a single page, it is not
|
||||
paginated as normal, and instead will return all results without
|
||||
obeying regular pagination parameters. The ordering is left undefined,
|
||||
it's up to the clients to re-order results as needed before displaying
|
||||
the add-ons to the end-users.
|
||||
|
||||
:query string app: Mandatory. Filter by :ref:`add-on application <addon-detail-application>` availability.
|
||||
:query string lang: Activate translations in the specific language for that query. (See :ref:`translated fields <api-overview-translations>`)
|
||||
:>json array results: An array of language tools.
|
||||
:>json int results[].id: The add-on id on AMO.
|
||||
:>json object results[].current_version: Object holding the current :ref:`version <version-detail-object>` of the add-on. For performance reasons the ``release_notes`` field is omitted and the ``license`` field omits the ``text`` property.
|
||||
:>json string results[].default_locale: The add-on default locale for translations.
|
||||
:>json string|object|null results[].name: The add-on name (See :ref:`translated fields <api-overview-translations>`).
|
||||
:>json string results[].locale_disambiguation: Free text field allowing clients to distinguish between multiple dictionaries in the same locale but different spellings. Only present when using the Language Tools endpoint.
|
||||
:>json string results[].target_locale: For dictionaries and language packs, the locale the add-on is meant for. Only present when using the Language Tools endpoint.
|
||||
:>json string results[].type: The :ref:`add-on type <addon-detail-type>`.
|
||||
:>json string results[].url: The (absolute) add-on detail URL.
|
||||
|
|
|
@ -7,7 +7,7 @@ from olympia.activity.views import VersionReviewNotesViewSet
|
|||
|
||||
from .views import (
|
||||
AddonFeaturedView, AddonSearchView, AddonVersionViewSet, AddonViewSet,
|
||||
StaticCategoryView)
|
||||
LanguageToolsView, StaticCategoryView)
|
||||
|
||||
|
||||
addons = SimpleRouter()
|
||||
|
@ -27,4 +27,6 @@ urlpatterns = [
|
|||
url(r'^search/$', AddonSearchView.as_view(), name='addon-search'),
|
||||
url(r'^featured/$', AddonFeaturedView.as_view(), name='addon-featured'),
|
||||
url(r'^categories/$', StaticCategoryView.as_view(), name='category-list'),
|
||||
url(r'^language-tools/$', LanguageToolsView.as_view(),
|
||||
name='addon-language-tools'),
|
||||
]
|
||||
|
|
|
@ -486,3 +486,14 @@ class StaticCategorySerializer(serializers.Serializer):
|
|||
|
||||
def get_type(self, obj):
|
||||
return ADDON_TYPE_CHOICES_API[obj.type]
|
||||
|
||||
|
||||
class LanguageToolsSerializer(AddonSerializer):
|
||||
target_locale = serializers.CharField()
|
||||
locale_disambiguation = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('id', 'current_version', 'default_locale',
|
||||
'locale_disambiguation', 'name', 'target_locale', 'type',
|
||||
'url', )
|
||||
|
|
|
@ -15,8 +15,8 @@ from olympia.addons.models import (
|
|||
Addon, AddonCategory, AddonUser, Category, Persona, Preview)
|
||||
from olympia.addons.serializers import (
|
||||
AddonSerializer, AddonSerializerWithUnlistedData, ESAddonSerializer,
|
||||
ESAddonSerializerWithUnlistedData, SimpleVersionSerializer,
|
||||
VersionSerializer)
|
||||
ESAddonSerializerWithUnlistedData, LanguageToolsSerializer,
|
||||
SimpleVersionSerializer, VersionSerializer)
|
||||
from olympia.addons.utils import generate_addon_guid
|
||||
from olympia.bandwagon.models import FeaturedCollection
|
||||
from olympia.constants.categories import CATEGORIES
|
||||
|
@ -688,3 +688,30 @@ class TestSimpleVersionSerializerOutput(TestCase):
|
|||
assert result['license']['name']['fr'] == u'Mä Licence'
|
||||
assert result['license']['url'] == 'http://license.example.com/'
|
||||
assert 'text' not in result['license']
|
||||
|
||||
|
||||
class TestLanguageToolsSerializerOutput(TestCase):
|
||||
def setUp(self):
|
||||
self.request = APIRequestFactory().get('/')
|
||||
|
||||
def serialize(self):
|
||||
serializer = LanguageToolsSerializer(context={'request': self.request})
|
||||
return serializer.to_representation(self.addon)
|
||||
|
||||
def test_basic(self):
|
||||
self.addon = addon_factory(
|
||||
type=amo.ADDON_DICT, target_locale='fr',
|
||||
locale_disambiguation=u'lolé')
|
||||
result = self.serialize()
|
||||
assert result['id'] == self.addon.pk
|
||||
assert result['default_locale'] == self.addon.default_locale
|
||||
assert result['locale_disambiguation'] == (
|
||||
self.addon.locale_disambiguation)
|
||||
assert result['name'] == {'en-US': self.addon.name}
|
||||
assert result['target_locale'] == self.addon.target_locale
|
||||
assert result['url'] == absolutify(self.addon.get_url_path())
|
||||
|
||||
addon_testcase = AddonSerializerOutputTestMixin()
|
||||
addon_testcase.addon = self.addon
|
||||
addon_testcase._test_version(
|
||||
self.addon.current_version, result['current_version'])
|
||||
|
|
|
@ -2858,3 +2858,55 @@ class TestStaticCategoryView(TestCase):
|
|||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
assert response['cache-control'] == 'max-age=21600'
|
||||
|
||||
|
||||
class TestLanguageToolsView(TestCase):
|
||||
client_class = APITestClient
|
||||
|
||||
def setUp(self):
|
||||
super(TestLanguageToolsView, self).setUp()
|
||||
self.url = reverse('addon-language-tools')
|
||||
|
||||
def test_wrong_app(self):
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 400
|
||||
|
||||
response = self.client.get(self.url, {'app': 'foo'})
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_basic(self):
|
||||
dictionary = addon_factory(type=amo.ADDON_DICT, target_locale='fr')
|
||||
dictionary_spelling_variant = addon_factory(
|
||||
type=amo.ADDON_DICT, target_locale='fr',
|
||||
locale_disambiguation='For spelling reform')
|
||||
language_pack = addon_factory(type=amo.ADDON_DICT, target_locale='es')
|
||||
|
||||
# These add-ons below should be ignored: they are either not public or
|
||||
# of the wrong type, not supporting the app we care about, or their
|
||||
# target locale is empty.
|
||||
addon_factory(
|
||||
type=amo.ADDON_DICT, target_locale='de',
|
||||
version_kw={'application': amo.THUNDERBIRD.id})
|
||||
addon_factory(
|
||||
type=amo.ADDON_DICT, target_locale='fr',
|
||||
version_kw={'channel': amo.RELEASE_CHANNEL_UNLISTED})
|
||||
addon_factory(
|
||||
type=amo.ADDON_LPAPP, target_locale='es',
|
||||
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
|
||||
status=amo.STATUS_NOMINATED)
|
||||
addon_factory(type=amo.ADDON_DICT, target_locale='')
|
||||
addon_factory(type=amo.ADDON_LPAPP, target_locale=None)
|
||||
addon_factory(target_locale='fr')
|
||||
|
||||
response = self.client.get(self.url, {'app': 'firefox'})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.content)
|
||||
assert len(data['results']) == 3
|
||||
expected = [dictionary, dictionary_spelling_variant, language_pack]
|
||||
|
||||
assert (
|
||||
set(item['id'] for item in data['results']) ==
|
||||
set(item.pk for item in expected))
|
||||
|
||||
assert 'locale_disambiguation' in data['results'][0]
|
||||
assert 'target_locale' in data['results'][0]
|
||||
|
|
|
@ -63,7 +63,7 @@ from .models import Addon, Persona, FrozenAddon, ReplacementAddon
|
|||
from .serializers import (
|
||||
AddonEulaPolicySerializer, AddonFeatureCompatibilitySerializer,
|
||||
AddonSerializer, AddonSerializerWithUnlistedData, ESAddonSerializer,
|
||||
VersionSerializer, StaticCategorySerializer)
|
||||
LanguageToolsSerializer, VersionSerializer, StaticCategorySerializer)
|
||||
from .utils import get_creatured_ids, get_featured_ids
|
||||
|
||||
|
||||
|
@ -859,3 +859,29 @@ class StaticCategoryView(ListAPIView):
|
|||
request, response, *args, **kwargs)
|
||||
patch_cache_control(response, max_age=60 * 60 * 6)
|
||||
return response
|
||||
|
||||
|
||||
class LanguageToolsView(ListAPIView):
|
||||
authentication_classes = []
|
||||
pagination_class = None
|
||||
permission_classes = []
|
||||
serializer_class = LanguageToolsSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
try:
|
||||
application_id = AddonAppFilterParam(self.request).get_value()
|
||||
except ValueError:
|
||||
raise ParseError('Invalid app parameter.')
|
||||
|
||||
types = (amo.ADDON_DICT, amo.ADDON_LPAPP)
|
||||
return Addon.objects.public().filter(
|
||||
appsupport__app=application_id, type__in=types,
|
||||
target_locale__isnull=False).exclude(target_locale='')
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
# Ignore pagination (return everything) but do wrap the data in a
|
||||
# 'results' property to mimic what the default implementation of list()
|
||||
# does in DRF.
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return Response({'results': serializer.data})
|
||||
|
|
Загрузка…
Ссылка в новой задаче