stand up an app creation api (bug 704206)
This commit is contained in:
Родитель
77f49f4a99
Коммит
abe801e7d6
|
@ -1,5 +1,7 @@
|
|||
import functools
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
|
||||
import commonware.log
|
||||
|
@ -7,16 +9,20 @@ import happyforms
|
|||
from piston.handler import AnonymousBaseHandler, BaseHandler
|
||||
from piston.utils import rc
|
||||
from tower import ugettext as _
|
||||
import waffle
|
||||
|
||||
import amo
|
||||
from access import acl
|
||||
from addons.forms import AddonForm
|
||||
from addons.models import Addon, AddonUser
|
||||
from amo.utils import paginate
|
||||
from devhub.forms import LicenseForm
|
||||
from devhub.forms import LicenseForm, NewManifestForm
|
||||
from devhub import tasks
|
||||
from files.models import FileUpload, Platform
|
||||
from users.models import UserProfile
|
||||
from versions.forms import XPIForm
|
||||
from versions.models import Version, ApplicationsVersions
|
||||
from webapps.models import Webapp
|
||||
|
||||
log = commonware.log.getLogger('z.api')
|
||||
|
||||
|
@ -151,6 +157,49 @@ class AddonsHandler(BaseHandler):
|
|||
'count': paginator.paginator.count}
|
||||
|
||||
|
||||
class AppsHandler(AddonsHandler):
|
||||
allowed_methods = ('GET', 'POST')
|
||||
model = Webapp
|
||||
|
||||
fields = ('id', 'name', 'manifest_url', 'status', 'app_slug')
|
||||
exclude = ('highest_status', 'icon_type')
|
||||
|
||||
@transaction.commit_on_success
|
||||
def create(self, request):
|
||||
if not waffle.flag_is_active(request, 'accept-webapps'):
|
||||
return rc.BAD_REQUEST
|
||||
|
||||
form = NewManifestForm(request.POST)
|
||||
if form.is_valid():
|
||||
# This feels like an awful lot of work.
|
||||
# But first upload the file and do the validation.
|
||||
upload = FileUpload.objects.create()
|
||||
tasks.fetch_manifest(form.cleaned_data['manifest'], upload.pk)
|
||||
|
||||
# We must reget the object here since the above has
|
||||
# saved changes to the object.
|
||||
upload = FileUpload.uncached.get(pk=upload.pk)
|
||||
# Check it validated correctly.
|
||||
if settings.VALIDATE_ADDONS:
|
||||
validation = json.loads(upload.validation)
|
||||
if validation['errors']:
|
||||
response = rc.BAD_REQUEST
|
||||
response.write(validation)
|
||||
return response
|
||||
|
||||
# Fetch the addon, the icon and set the user.
|
||||
addon = Addon.from_upload(upload,
|
||||
[Platform.objects.get(id=amo.PLATFORM_ALL.id)])
|
||||
tasks.fetch_icon(addon)
|
||||
AddonUser(addon=addon, user=request.amo_user).save()
|
||||
addon.update(status=amo.STATUS_PENDING if
|
||||
settings.WEBAPPS_RESTRICTED else amo.STATUS_PUBLIC)
|
||||
|
||||
else:
|
||||
return _form_error(form)
|
||||
return addon
|
||||
|
||||
|
||||
class ApplicationsVersionsHandler(AnonymousBaseHandler):
|
||||
model = ApplicationsVersions
|
||||
allowed_methods = ('GET', )
|
||||
|
@ -276,7 +325,7 @@ class AMOBaseHandler(BaseHandler):
|
|||
def delete(self, request, id):
|
||||
try:
|
||||
return self.model.objects.get(pk=id).delete()
|
||||
except Performance.DoesNotExist:
|
||||
except self.model.DoesNotExist:
|
||||
return rc.NOT_HERE
|
||||
|
||||
def create(self, request):
|
||||
|
@ -289,7 +338,7 @@ class AMOBaseHandler(BaseHandler):
|
|||
if id:
|
||||
try:
|
||||
return self.model.objects.get(pk=id)
|
||||
except Performance.DoesNotExist:
|
||||
except self.model.DoesNotExist:
|
||||
return rc.NOT_HERE
|
||||
else:
|
||||
paginator = paginate(request, self.model.objects.all())
|
||||
|
@ -300,7 +349,7 @@ class AMOBaseHandler(BaseHandler):
|
|||
def update(self, request, id):
|
||||
try:
|
||||
obj = self.model.objects.get(pk=id)
|
||||
except Performance.DoesNotExist:
|
||||
except self.model.DoesNotExist:
|
||||
return rc.NOT_HERE
|
||||
form = self.get_form(request.POST, instance=obj)
|
||||
if form.is_valid():
|
||||
|
|
|
@ -34,6 +34,7 @@ from django.test.client import (encode_multipart, Client, FakePayload,
|
|||
|
||||
import oauth2 as oauth
|
||||
from mock import Mock, patch
|
||||
from nose import SkipTest
|
||||
from nose.tools import eq_
|
||||
from piston.models import Consumer
|
||||
|
||||
|
@ -202,6 +203,9 @@ class BaseOAuth(TestCase):
|
|||
def _login(self):
|
||||
self.client.login(username='admin@mozilla.com', password='password')
|
||||
|
||||
|
||||
class TestBaseOAuth(BaseOAuth):
|
||||
|
||||
def test_accepted(self):
|
||||
self.assertRaises(AssertionError, get_request_token,
|
||||
self.accepted_consumer)
|
||||
|
@ -748,6 +752,46 @@ class TestAddon(BaseOAuth):
|
|||
eq_(json.loads(r.content)['count'], 0)
|
||||
|
||||
|
||||
@patch.object(settings, 'VALIDATE_ADDONS', False)
|
||||
class TestCreateApp(BaseOAuth):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCreateApp, self).setUp()
|
||||
|
||||
patcher = patch('devhub.tasks._fetch_content')
|
||||
|
||||
response_mock = Mock()
|
||||
response_mock.read.return_value = '{"name": "Some App"}'
|
||||
response_mock.headers = {'Content-Type':
|
||||
'application/x-web-app-manifest+json'}
|
||||
|
||||
self.urlopen_mock = patcher.start()
|
||||
self.urlopen_mock.return_value = response_mock
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
patcher = patch('waffle.flag_is_active')
|
||||
patcher.start().return_value = True
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def make_create_request(self, data):
|
||||
return client.post('api.apps', self.accepted_consumer, self.token,
|
||||
data=data)
|
||||
|
||||
def test_create_app(self):
|
||||
res = self.make_create_request({'manifest': 'http://x.com/a.webapp'})
|
||||
eq_(res.status_code, 200)
|
||||
|
||||
def test_no_manifest(self):
|
||||
res = self.make_create_request({'manifest': ''})
|
||||
eq_(res.status_code, 400)
|
||||
assert 'manifest' in res.content
|
||||
|
||||
def test_validation_fails(self):
|
||||
raise SkipTest
|
||||
# TODO(andym) figure out how to stop it doing validation, but
|
||||
# coping with a bad validation.
|
||||
|
||||
|
||||
class TestPerformanceAPI(BaseOAuth):
|
||||
fixtures = ['base/users']
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ for regexp in list_regexps:
|
|||
ad = {'authentication': authentication.AMOOAuthAuthentication(two_legged=True)}
|
||||
user_resource = Resource(handler=handlers.UserHandler, **ad)
|
||||
addons_resource = Resource(handler=handlers.AddonsHandler, **ad)
|
||||
apps_resource = Resource(handler=handlers.AppsHandler, **ad)
|
||||
version_resource = Resource(handler=handlers.VersionsHandler, **ad)
|
||||
|
||||
piston_patterns = patterns('',
|
||||
|
@ -76,7 +77,9 @@ piston_patterns = patterns('',
|
|||
url(r'^addon/%s/versions$' % ADDON_ID, version_resource,
|
||||
name='api.versions'),
|
||||
url(r'^addon/%s/version/(?P<version_id>\d+)$' % ADDON_ID,
|
||||
version_resource, name='api.version')
|
||||
version_resource, name='api.version'),
|
||||
url(r'^apps/$', apps_resource, name='api.apps'),
|
||||
url(r'^app/%s$' % ADDON_ID, apps_resource, name='api.app'),
|
||||
)
|
||||
|
||||
urlpatterns = patterns('',
|
||||
|
|
|
@ -185,7 +185,7 @@ class File(amo.models.OnChangeMixin, amo.models.ModelBase):
|
|||
data = {'sdkVersion': None, 'builderVersion': None}
|
||||
try:
|
||||
zip_ = zipfile.ZipFile(path)
|
||||
except zipfile.BadZipfile:
|
||||
except (zipfile.BadZipfile, IOError):
|
||||
# This path is not an XPI. It's probably an app manifest.
|
||||
return data
|
||||
name = 'harness-options.json'
|
||||
|
|
Загрузка…
Ссылка в новой задаче