just the start, create an app (bug 753570)
This commit is contained in:
Родитель
c3346ea621
Коммит
cdcbc6ba97
|
@ -288,6 +288,13 @@ class AMOPaths(object):
|
|||
os.makedirs(os.path.dirname(file.file_path))
|
||||
shutil.copyfile(self.xpi_path(name), file.file_path)
|
||||
|
||||
def manifest_path(self, name):
|
||||
return os.path.join(settings.ROOT,
|
||||
'mkt/submit/tests/webapps/%s' % name)
|
||||
|
||||
def manifest_copy_over(self, dest, name):
|
||||
shutil.copyfile(self.manifest_path(name), dest)
|
||||
|
||||
@staticmethod
|
||||
def sample_key():
|
||||
path = 'mkt/webapps/tests/sample.key'
|
||||
|
|
|
@ -206,6 +206,11 @@ ADDON_PREMIUM_TYPES = {
|
|||
ADDON_PREMIUM_OTHER: _("Premium, but I'll use my own payments system")
|
||||
}
|
||||
|
||||
# Non-locale versions for the API.
|
||||
ADDON_PREMIUM_API = {
|
||||
ADDON_FREE: 'free',
|
||||
}
|
||||
|
||||
ADDON_PREMIUMS = (ADDON_PREMIUM, ADDON_PREMIUM_INAPP)
|
||||
ADDON_FREES = (ADDON_FREE, ADDON_FREE_INAPP)
|
||||
ADDON_INAPPS = (ADDON_PREMIUM_INAPP, ADDON_FREE_INAPP)
|
||||
|
|
|
@ -5,8 +5,11 @@ from tastypie.bundle import Bundle
|
|||
from tastypie.resources import ModelResource
|
||||
from tastypie.utils import dict_strip_unicode_keys
|
||||
|
||||
from translations.fields import PurifiedField, TranslatedField
|
||||
|
||||
|
||||
class MarketplaceResource(ModelResource):
|
||||
|
||||
def get_resource_uri(self, bundle_or_obj):
|
||||
# Fix until my pull request gets pulled into tastypie.
|
||||
# https://github.com/toastdriven/django-tastypie/pull/490
|
||||
|
@ -49,5 +52,13 @@ class MarketplaceResource(ModelResource):
|
|||
response_class=http.HttpCreated,
|
||||
location=location)
|
||||
|
||||
@classmethod
|
||||
def should_skip_field(cls, field):
|
||||
# We don't want to skip translated fields.
|
||||
if isinstance(field, (PurifiedField, TranslatedField)):
|
||||
return False
|
||||
|
||||
return True if getattr(field, 'rel') else False
|
||||
|
||||
def form_errors(self, form):
|
||||
return json.dumps({'error_message': dict(form.errors.items())})
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
from django import forms
|
||||
|
||||
import happyforms
|
||||
|
||||
from files.models import FileUpload
|
||||
|
||||
|
||||
class UploadForm(happyforms.Form):
|
||||
manifest = forms.CharField(max_length=32, min_length=32)
|
||||
|
||||
def clean_manifest(self):
|
||||
uuid = self.cleaned_data.get('manifest')
|
||||
try:
|
||||
upload = FileUpload.objects.get(uuid=uuid)
|
||||
except FileUpload.DoesNotExist:
|
||||
raise forms.ValidationError('No upload found.')
|
||||
if not upload.valid:
|
||||
raise forms.ValidationError('Upload not valid.')
|
||||
|
||||
self.obj = upload
|
||||
return uuid
|
||||
|
||||
|
|
@ -1,17 +1,23 @@
|
|||
import json
|
||||
|
||||
from django.db import transaction
|
||||
from django.forms import ValidationError
|
||||
|
||||
import commonware.log
|
||||
from tastypie import http
|
||||
from tastypie.exceptions import ImmediateHttpResponse
|
||||
|
||||
from files.models import FileUpload
|
||||
from mkt.developers import tasks
|
||||
from mkt.developers.forms import NewManifestForm
|
||||
from addons.models import AddonUser
|
||||
import amo
|
||||
from amo.decorators import write
|
||||
from files.models import FileUpload, Platform
|
||||
from mkt.api.authentication import (OwnerAuthorization,
|
||||
MarketplaceAuthentication)
|
||||
from mkt.api.base import MarketplaceResource
|
||||
from mkt.api.forms import UploadForm
|
||||
from mkt.developers import tasks
|
||||
from mkt.developers.forms import NewManifestForm
|
||||
from mkt.webapps.models import Webapp
|
||||
|
||||
log = commonware.log.getLogger('z.api')
|
||||
|
||||
|
@ -30,6 +36,8 @@ class ValidationResource(MarketplaceResource):
|
|||
authorization = OwnerAuthorization()
|
||||
resource_name = 'validation'
|
||||
|
||||
@write
|
||||
@transaction.commit_on_success
|
||||
def obj_create(self, bundle, request=None, **kwargs):
|
||||
form = NewManifestForm(bundle.data)
|
||||
if not form.is_valid():
|
||||
|
@ -65,3 +73,42 @@ class ValidationResource(MarketplaceResource):
|
|||
bundle.data['processed'] = (bool(bundle.obj.valid or
|
||||
bundle.obj.validation))
|
||||
return bundle
|
||||
|
||||
|
||||
class AppResource(MarketplaceResource):
|
||||
|
||||
class Meta:
|
||||
queryset = Webapp.objects.valid().no_transforms()
|
||||
fields = ['name', 'description', 'homepage', 'status', 'summary',
|
||||
'support_email', 'support_url']
|
||||
list_allowed_methods = ['post']
|
||||
allowed_methods = []
|
||||
always_return_data = True
|
||||
authentication = MarketplaceAuthentication()
|
||||
authorization = OwnerAuthorization()
|
||||
resource_name = 'app'
|
||||
|
||||
@write
|
||||
@transaction.commit_on_success
|
||||
def obj_create(self, bundle, request, **kwargs):
|
||||
form = UploadForm(bundle.data)
|
||||
if not form.is_valid():
|
||||
raise ValidationError(self.form_errors(form))
|
||||
|
||||
if not (OwnerAuthorization()
|
||||
.is_authorized(request, object=form.obj)):
|
||||
raise ImmediateHttpResponse(response=http.HttpUnauthorized())
|
||||
|
||||
plats = [Platform.objects.get(id=amo.PLATFORM_ALL.id)]
|
||||
|
||||
# Create app, user and fetch the icon.
|
||||
bundle.obj = Webapp.from_upload(form.obj, plats)
|
||||
AddonUser(addon=bundle.obj, user=request.amo_user).save()
|
||||
tasks.fetch_icon.delay(bundle.obj,)
|
||||
return bundle
|
||||
|
||||
def dehydrate(self, bundle):
|
||||
obj = bundle.obj
|
||||
bundle.data['slug'] = obj.app_slug
|
||||
bundle.data['premium_type'] = amo.ADDON_PREMIUM_API[obj.premium_type]
|
||||
return bundle
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import json
|
||||
import tempfile
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from mock import patch
|
||||
from nose.tools import eq_
|
||||
|
||||
from addons.models import Addon
|
||||
from amo.tests import AMOPaths
|
||||
from files.models import FileUpload
|
||||
from mkt.api.tests.test_oauth import BaseOAuth
|
||||
from mkt.webapps.models import Webapp
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
|
@ -113,3 +117,67 @@ class TestGetValidationHandler(ValidationHandler):
|
|||
eq_(data['processed'], True)
|
||||
eq_(data['valid'], False)
|
||||
eq_(data['validation'], json.loads(error))
|
||||
|
||||
|
||||
class CreateHandler(BaseOAuth):
|
||||
fixtures = ['base/user_2519', 'base/users', 'base/platforms']
|
||||
|
||||
def setUp(self):
|
||||
super(CreateHandler, self).setUp()
|
||||
self.list_url = ('api_dispatch_list', {'resource_name': 'app'})
|
||||
self.user = UserProfile.objects.get(pk=2519)
|
||||
self.file = tempfile.NamedTemporaryFile('w', suffix='.webapp').name
|
||||
self.manifest_copy_over(self.file, 'mozball-nice-slug.webapp')
|
||||
|
||||
def create(self):
|
||||
return FileUpload.objects.create(user=self.user, path=self.file,
|
||||
name=self.file, valid=True)
|
||||
|
||||
def get_error(self, response):
|
||||
return json.loads(response.content)['error_message']
|
||||
|
||||
|
||||
@patch.object(settings, 'SITE_URL', 'http://api/')
|
||||
class TestAppCreateHandler(CreateHandler, AMOPaths):
|
||||
|
||||
def count(self):
|
||||
return Addon.objects.count()
|
||||
|
||||
def test_not_valid(self):
|
||||
obj = self.create()
|
||||
obj.update(valid=False)
|
||||
res = self.client.post(self.list_url,
|
||||
body=json.dumps({'manifest': obj.uuid}))
|
||||
eq_(res.status_code, 400)
|
||||
eq_(self.get_error(res)['manifest'], ['Upload not valid.'])
|
||||
eq_(self.count(), 0)
|
||||
|
||||
def test_not_there(self):
|
||||
res = self.client.post(self.list_url,
|
||||
body=json.dumps({'manifest':
|
||||
'some-random-32-character-stringy'}))
|
||||
eq_(res.status_code, 400)
|
||||
eq_(self.get_error(res)['manifest'], ['No upload found.'])
|
||||
eq_(self.count(), 0)
|
||||
|
||||
def test_not_yours(self):
|
||||
obj = self.create()
|
||||
obj.update(user=UserProfile.objects.get(email='admin@mozilla.com'))
|
||||
res = self.client.post(self.list_url,
|
||||
body=json.dumps({'manifest': obj.uuid}))
|
||||
eq_(res.status_code, 401)
|
||||
eq_(self.count(), 0)
|
||||
|
||||
def test_create(self):
|
||||
obj = self.create()
|
||||
res = self.client.post(self.list_url,
|
||||
body=json.dumps({'manifest': obj.uuid}))
|
||||
eq_(res.status_code, 201)
|
||||
content = json.loads(res.content)
|
||||
eq_(content['status'], 0)
|
||||
eq_(content['slug'], u'mozillaball')
|
||||
eq_(content['support_email'], None)
|
||||
eq_(self.count(), 1)
|
||||
|
||||
app = Webapp.objects.get(app_slug=content['slug'])
|
||||
eq_(set(app.authors.all()), set([self.user]))
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from django.conf.urls.defaults import include, patterns, url
|
||||
|
||||
from tastypie.api import Api
|
||||
from mkt.api.resources import ValidationResource
|
||||
from mkt.api.resources import AppResource, ValidationResource
|
||||
|
||||
api = Api(api_name='apps')
|
||||
api.register(ValidationResource())
|
||||
api.register(AppResource())
|
||||
|
||||
validation = Api(api_name='apps')
|
||||
validation.register(ValidationResource())
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^', include(validation.urls)),
|
||||
url(r'^', include(api.urls)),
|
||||
)
|
||||
|
|
Загрузка…
Ссылка в новой задаче