app handler in the api (bug 777171)

This commit is contained in:
Andy McKay 2012-11-06 17:39:37 -08:00
Родитель 00a8081f97
Коммит d19b4c3146
4 изменённых файлов: 176 добавлений и 19 удалений

Просмотреть файл

@ -464,6 +464,10 @@ class AMOPaths(object):
return os.path.join(
settings.ROOT, 'mkt/submit/tests/packaged/%s' % name)
def packaged_copy_over(self, dest, name):
with storage.open(dest, 'wb') as f:
copyfileobj(open(self.packaged_app_path(name)), f)
def close_to_now(dt):
"""

Просмотреть файл

@ -9,14 +9,24 @@ import happyforms
from addons.models import Addon, Category
import amo
from files.models import FileUpload
from mkt.developers.forms import NewPackagedAppForm
from mkt.developers.utils import check_upload
class UploadForm(happyforms.Form):
manifest = forms.CharField(max_length=32, min_length=32)
is_packaged = False
manifest = forms.CharField(max_length=32, min_length=32, required=False)
upload = forms.CharField(max_length=32, min_length=32, required=False)
def clean(self):
uuid = self.cleaned_data.get('upload', '')
if uuid:
self.is_packaged = True
else:
uuid = self.cleaned_data.get('manifest', '')
if not uuid:
raise forms.ValidationError('No upload or manifest specified.')
def clean_manifest(self):
uuid = self.cleaned_data.get('manifest')
try:
upload = FileUpload.objects.get(uuid=uuid)
except FileUpload.DoesNotExist:
@ -41,19 +51,47 @@ class JSONField(forms.Field):
return value
def parse(file_, require_name=False, require_type=None):
try:
if not set(['data', 'type']).issubset(set(file_.keys())):
raise forms.ValidationError('Type and data are required.')
except AttributeError:
raise forms.ValidationError('File must be a dictionary.')
try:
data = base64.b64decode(file_['data'])
except TypeError:
raise forms.ValidationError('File must be base64 encoded.')
result = StringIO.StringIO(data)
result.size = len(data)
if require_type and file_.get('type', '') != require_type:
raise forms.ValidationError('Type must be %s.' % require_type)
if require_name and not file_.get('name', ''):
raise forms.ValidationError('Name not specified.')
result.name = file_.get('name', '')
return result
class NewPackagedForm(NewPackagedAppForm):
upload = JSONField()
def clean_upload(self):
self.cleaned_data['upload'] = parse(self.cleaned_data
.get('upload', {}),
require_name=True,
require_type='application/zip')
return super(NewPackagedForm, self).clean_upload()
class PreviewJSONForm(happyforms.Form):
file = JSONField(required=True)
position = forms.IntegerField(required=True)
def clean_file(self):
file_ = self.cleaned_data.get('file', {})
try:
if not set(['data', 'type']).issubset(set(file_.keys())):
raise forms.ValidationError('Type and data are required.')
except AttributeError:
raise forms.ValidationError('File must be a dictionary.')
file_obj = StringIO.StringIO(base64.b64decode(file_['data']))
file_obj = parse(file_)
errors, hash_ = check_upload(file_obj, 'preview', file_['type'])
if errors:
raise forms.ValidationError(errors)

Просмотреть файл

@ -20,11 +20,11 @@ from files.models import FileUpload, Platform
from mkt.api.authentication import (AppOwnerAuthorization, OwnerAuthorization,
MarketplaceAuthentication)
from mkt.api.base import MarketplaceResource
from mkt.api.forms import CategoryForm, UploadForm, StatusForm
from mkt.api.forms import (CategoryForm, NewPackagedForm, UploadForm,
PreviewJSONForm, StatusForm)
from mkt.developers import tasks
from mkt.developers.forms import NewManifestForm
from mkt.developers.forms import PreviewForm
from mkt.api.forms import PreviewJSONForm
from mkt.webapps.models import Addon
from mkt.submit.forms import AppDetailsBasicForm
@ -49,13 +49,25 @@ class ValidationResource(MarketplaceResource):
@write
@transaction.commit_on_success
def obj_create(self, bundle, request=None, **kwargs):
form = NewManifestForm(bundle.data)
packaged = 'upload' in bundle.data
form = (NewPackagedForm(bundle.data) if packaged
else NewManifestForm(bundle.data))
if not form.is_valid():
raise self.form_errors(form)
upload = FileUpload.objects.create(user=amo.get_user())
tasks.fetch_manifest(form.cleaned_data['manifest'], upload.pk)
bundle.obj = FileUpload.objects.get(pk=upload.pk)
if not packaged:
upload = FileUpload.objects.create(user=amo.get_user())
# The hosted app validator is pretty fast.
tasks.fetch_manifest(form.cleaned_data['manifest'], upload.pk)
else:
upload = form.file_upload
# The packaged app validator is much heavier.
tasks.validator.delay(upload.pk)
# This is a reget of the object, we do this to get the refreshed
# results if not celery delayed.
bundle.obj = FileUpload.uncached.get(pk=upload.pk)
log.info('Validation created: %s' % bundle.obj.pk)
return bundle
@ -107,6 +119,7 @@ class AppResource(MarketplaceResource):
@transaction.commit_on_success
def obj_create(self, bundle, request, **kwargs):
form = UploadForm(bundle.data)
if not form.is_valid():
raise self.form_errors(form)
@ -117,7 +130,8 @@ class AppResource(MarketplaceResource):
plats = [Platform.objects.get(id=amo.PLATFORM_ALL.id)]
# Create app, user and fetch the icon.
bundle.obj = Addon.from_upload(form.obj, plats)
bundle.obj = Addon.from_upload(form.obj, plats,
is_packaged=form.is_packaged)
AddonUser(addon=bundle.obj, user=request.amo_user).save()
self._icons_and_images(bundle.obj)

Просмотреть файл

@ -75,6 +75,70 @@ class TestAddValidationHandler(ValidationHandler):
eq_(self.get_error(res)['manifest'], ['Enter a valid URL.'])
@patch.object(settings, 'SITE_URL', 'http://api/')
class TestPackagedValidation(amo.tests.AMOPaths, ValidationHandler):
def setUp(self):
super(TestPackagedValidation, self).setUp()
name = 'mozball.zip'
path = self.packaged_app_path(name)
self.file = base64.b64encode(open(path).read())
self.data = {'data': self.file, 'name': name,
'type': 'application/zip'}
def create(self):
res = self.client.post(self.list_url,
data=json.dumps({'upload': self.data}))
if res.status_code < 400:
self.get_url = ('api_dispatch_detail',
{'resource_name': 'validation',
'pk': json.loads(res.content)['id']})
return res
def test_good(self):
res = self.create()
eq_(res.status_code, 201) # Note! This should be a 202.
content = json.loads(res.content)
eq_(content['processed'], True)
obj = FileUpload.objects.get(uuid=content['id'])
eq_(obj.user, self.user)
@patch('mkt.developers.forms.MAX_PACKAGED_APP_SIZE', 1)
def test_too_big(self):
res = self.create()
eq_(res.status_code, 400)
obj = FileUpload.objects.get()
messages = json.loads(obj.validation)['messages']
eq_(messages[0]['message'],
["Packaged app too large for submission.",
"Packages must be less than 1 byte."])
def form_errors(self, data, errors):
self.data = data
res = self.create()
eq_(res.status_code, 400)
eq_(self.get_error(res)['upload'], errors)
def test_missing(self):
self.form_errors({'data': self.file, 'name': 'mozball.zip'},
[u'Type and data are required.'])
def test_missing_name(self):
self.form_errors({'data': self.file, 'type': 'application/zip'},
[u'Name not specified.'])
def test_wrong(self):
self.form_errors({'data': self.file, 'name': 'mozball.zip',
'type': 'application/foo'},
[u'Type must be application/zip.'])
def test_invalid(self):
self.form_errors({'data': 'x', 'name': 'mozball.zip',
'type': 'application/foo'},
[u'File must be base64 encoded.'])
@patch.object(settings, 'SITE_URL', 'http://api/')
class TestGetValidationHandler(ValidationHandler):
@ -186,7 +250,7 @@ class TestAppCreateHandler(CreateHandler, AMOPaths):
res = self.client.post(self.list_url,
data=json.dumps({'manifest': obj.uuid}))
eq_(res.status_code, 400)
eq_(self.get_error(res)['manifest'], ['Upload not valid.'])
eq_(self.get_error(res)['__all__'], ['Upload not valid.'])
eq_(self.count(), 0)
def test_not_there(self):
@ -194,7 +258,7 @@ class TestAppCreateHandler(CreateHandler, AMOPaths):
data=json.dumps({'manifest':
'some-random-32-character-stringy'}))
eq_(res.status_code, 400)
eq_(self.get_error(res)['manifest'], ['No upload found.'])
eq_(self.get_error(res)['__all__'], ['No upload found.'])
eq_(self.count(), 0)
def test_not_yours(self):
@ -345,6 +409,43 @@ class TestAppCreateHandler(CreateHandler, AMOPaths):
eq_(res.status_code, 404)
class CreatePackagedHandler(amo.tests.AMOPaths, BaseOAuth):
fixtures = ['base/user_2519', 'base/users', 'base/platforms']
def setUp(self):
super(CreatePackagedHandler, self).setUp()
self.list_url = ('api_dispatch_list', {'resource_name': 'app'})
self.user = UserProfile.objects.get(pk=2519)
self.file = tempfile.NamedTemporaryFile('w', suffix='.zip').name
self.packaged_copy_over(self.file, 'mozball.zip')
self.categories = []
for x in range(0, 2):
self.categories.append(Category.objects.create(
name='cat-%s' % x,
type=amo.ADDON_WEBAPP))
def create(self):
return FileUpload.objects.create(user=self.user, path=self.file,
name=self.file, valid=True)
@patch.object(settings, 'SITE_URL', 'http://api/')
class TestPackagedAppCreateHandler(CreatePackagedHandler):
fixtures = ['base/user_2519', 'base/users',
'base/platforms', 'base/apps', 'base/appversion']
def test_create(self):
obj = self.create()
res = self.client.post(self.list_url,
data=json.dumps({'upload': obj.uuid}))
eq_(res.status_code, 201)
content = json.loads(res.content)
eq_(content['status'], 0)
# Note the packaged status is not returned in the result.
app = Webapp.objects.get(app_slug=content['slug'])
eq_(app.is_packaged, True)
@patch.object(settings, 'SITE_URL', 'http://api/')
class TestAppStatusHandler(CreateHandler, AMOPaths):