app handler in the api (bug 777171)
This commit is contained in:
Родитель
00a8081f97
Коммит
d19b4c3146
|
@ -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):
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче