Skip to content
This repository has been archived by the owner on Mar 15, 2018. It is now read-only.

Commit

Permalink
Implement opensearch suggestions in the API (bug 898020)
Browse files Browse the repository at this point in the history
  • Loading branch information
diox committed Jul 31, 2013
1 parent 7b28703 commit 5195fdb
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 1 deletion.
18 changes: 18 additions & 0 deletions mkt/api/serializers.py
Expand Up @@ -27,3 +27,21 @@ def deserialize(self, content, format='application/json'):
return super(Serializer, self).deserialize(content, format)
except JSONDecodeError, exc:
raise DeserializationError(original=exc)


class SuggestionsSerializer(Serializer):
formats = ['suggestions+json', 'json']
content_types = {
'suggestions+json': 'application/x-suggestions+json',
'json': 'application/json',
}

def serialize(self, bundle, format='application/json', options=None):
if options is None:
options = {}
if format == 'application/x-suggestions+json':
# Format application/x-suggestions+json just like regular json.
format = 'application/json'
return super(SuggestionsSerializer, self).serialize(bundle,
format=format,
options=options)
3 changes: 2 additions & 1 deletion mkt/api/urls.py
Expand Up @@ -11,14 +11,15 @@
from mkt.features.views import AppFeaturesList
from mkt.stats.api import GlobalStatsResource
from mkt.ratings.resources import RatingResource
from mkt.search.api import SearchResource
from mkt.search.api import SearchResource, SuggestionsResource


api = Api(api_name='apps')
api.register(ValidationResource())
api.register(AppResource())
api.register(PreviewResource())
api.register(SearchResource())
api.register(SuggestionsResource())
api.register(StatusResource())
api.register(RatingResource())

Expand Down
50 changes: 50 additions & 0 deletions mkt/search/api.py
Expand Up @@ -9,6 +9,7 @@
OptionalOAuthAuthentication)
from mkt.api.base import CORSResource, MarketplaceResource
from mkt.api.resources import AppResource
from mkt.api.serializers import SuggestionsSerializer
from mkt.constants.features import FeatureProfile
from mkt.search.views import _filter_search, _get_query
from mkt.search.forms import ApiSearchForm
Expand Down Expand Up @@ -149,3 +150,52 @@ def alter_list_data_to_serialize(self, request, data):
# Alter the _view_name so that statsd logs seperately from search.
request._view_name = 'featured'
return data


class SuggestionsResource(SearchResource):

class Meta(SearchResource.Meta):
authorization = ReadOnlyAuthorization()
fields = ['name', 'manifest_url']
resource_name = 'suggest'
limit = 10
serializer = SuggestionsSerializer(['suggestions+json'])

def determine_format(self, request):
return 'application/x-suggestions+json'

def get_search_data(self, request):
data = super(SuggestionsResource, self).get_search_data(request)
self.query = data.get('q', '')
return data

def alter_list_data_to_serialize(self, request, data):
return data

def paginate_results(self, request, qs):
return self.rehydrate_results(request, qs[:self._meta.limit])

def rehydrate_results(self, request, qs):
names = []
descriptions = []
urls = []
icons = []
for obj in qs:
# Tastypie expects obj.pk to be present, so set it manually.
obj.pk = obj.id
data = self.full_dehydrate(self.build_bundle(obj=obj,
request=request))
names.append(data['name'])
descriptions.append(data['description'])
urls.append(data['absolute_url'])
icons.append(data['icon'])
return [self.query, names, descriptions, urls, icons]

def dehydrate(self, bundle):
data = super(SuggestionsResource, self).dehydrate(bundle).data
return {
'description': data['description'],
'name': data['name'],
'absolute_url': data['absolute_url'],
'icon': data['icons'][64],
}
35 changes: 35 additions & 0 deletions mkt/search/tests/test_api.py
@@ -1,12 +1,16 @@
# -*- coding: utf-8 -*-
import json

from django.conf import settings

from mock import Mock, patch
from nose.tools import eq_, ok_

import amo
import mkt.regions
from addons.models import (AddonCategory, AddonDeviceType, AddonUpsell,
Category)
from amo.helpers import absolutify
from amo.tests import app_factory, ESTestCase
from stats.models import ClientData
from users.models import UserProfile
Expand Down Expand Up @@ -540,3 +544,34 @@ def test_non_matching_profile_desktop_with_category(self):
eq_(res.status_code, 200)
eq_(len(res.json['featured']), 1)
eq_(int(res.json['featured'][0]['id']), self.app.pk)


@patch.object(settings, 'SITE_URL', 'http://testserver')
class TestSuggestionsApi(ESTestCase):
fixtures = fixture('webapp_337141')

def setUp(self):
self.url = list_url('suggest')
self.refresh('webapp')
self.client = OAuthClient(None)

def test_suggestions(self):
app1 = Webapp.objects.get(pk=337141)
app1.save()
app2 = app_factory(name=u"Second âpp", description=u"Second dèsc",
created=self.days_ago(3))
self.refresh('webapp')

response = self.client.get(self.url)
parsed = json.loads(response.content)
eq_(parsed[0], '')
eq_(parsed[1], [unicode(app1.name), unicode(app2.name)])
eq_(parsed[2], [unicode(app1.description), unicode(app2.description)])
eq_(parsed[3], [absolutify(app1.get_detail_url()),
absolutify(app2.get_detail_url())])
eq_(parsed[4], [app1.get_icon_url(64), app2.get_icon_url(64)])

# Cleanup to remove these from the index.
unindex_webapps([app1.id, app2.id])
app1.delete()
app2.delete()

0 comments on commit 5195fdb

Please sign in to comment.