diff --git a/combo/apps/assets/api_views.py b/combo/apps/assets/api_views.py new file mode 100644 index 00000000..37f07a65 --- /dev/null +++ b/combo/apps/assets/api_views.py @@ -0,0 +1,64 @@ +# combo - content management system +# Copyright (C) 2019 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import base64 + +from django.core.files import File +from django.utils.six import BytesIO + +from rest_framework import serializers, permissions, status +from rest_framework.generics import GenericAPIView +from rest_framework.response import Response + +from .models import Asset + + +class FileSerializer(serializers.Serializer): + content = serializers.CharField(required=True, allow_blank=False) + content_type = serializers.CharField(required=False, allow_null=True) + filename = serializers.CharField(required=False, allow_null=True) + + def validate_content(self, value): + try: + return base64.decodestring(value.encode('ascii')) + except Exception as e: + raise serializers.ValidationError('content must be base64 (%r)' % e) + + +class AssetSerializer(serializers.Serializer): + asset = FileSerializer(required=True) + + +class Set(GenericAPIView): + permission_classes = (permissions.IsAuthenticated,) + serializer_class = AssetSerializer + + def post(self, request, key, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + if not serializer.is_valid(): + response = {'err': 1, 'err_desc': serializer.errors} + return Response(response, status.HTTP_400_BAD_REQUEST) + data = serializer.validated_data + + asset, created = Asset.objects.get_or_create(key=key) + asset.asset = File( + BytesIO(data['asset']['content']), + name=data['asset'].get('filename')) + asset.save() + response = {'err': 0} + return Response(response) + +view_set = Set.as_view() diff --git a/combo/apps/assets/urls.py b/combo/apps/assets/urls.py index 38770b91..ebdb8494 100644 --- a/combo/apps/assets/urls.py +++ b/combo/apps/assets/urls.py @@ -19,6 +19,7 @@ from django.conf.urls import url, include from combo.urls_utils import decorated_includes, manager_required from . import views +from . import api_views assets_manager_urls = [ url(r'^$', views.assets, name='combo-manager-assets'), @@ -35,4 +36,7 @@ urlpatterns = [ url(r'^assets/(?P[\w_:-]+)$', views.serve_asset), url(r'^manage/assets/', decorated_includes(manager_required, include(assets_manager_urls))), + + url('^api/assets/set/(?P[\w_:-]+)/$', api_views.view_set, + name='api-assets-set'), ] diff --git a/tests/test_assets.py b/tests/test_assets.py new file mode 100644 index 00000000..116dfc36 --- /dev/null +++ b/tests/test_assets.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import base64 + +from django.core.urlresolvers import reverse + +import pytest + +from combo.apps.assets.models import Asset + +pytestmark = pytest.mark.django_db + +def test_asset_set_api(app, john_doe): + app.authorization = ('Basic', (john_doe.username, john_doe.username)) + resp = app.post_json(reverse('api-assets-set', kwargs={'key': 'plop'}), params={ + 'asset': { + 'content': base64.encodestring(b'plop').decode('ascii'), + 'content_type': 'text/plain', + 'filename': 'plop.txt', + } + }) + assert Asset.objects.get(key='plop').asset.read() == b'plop' + + resp = app.post_json(reverse('api-assets-set', kwargs={'key': 'plop'}), params={ + 'asset': { + 'content': base64.encodestring(b'plop2').decode('ascii'), + 'content_type': 'text/plain', + 'filename': 'plop.txt', + } + }) + assert Asset.objects.get(key='plop').asset.read() == b'plop2' + + resp = app.post_json(reverse('api-assets-set', kwargs={'key': 'plop'}), params={}, status=400) + assert resp.json.get('err') == 1 + + for invalid_value in (None, 'not base 64', u'éléphant'): + resp = app.post_json(reverse('api-assets-set', kwargs={'key': 'plop'}), params={ + 'asset': { + 'content': invalid_value, + 'content_type': 'text/plain', + 'filename': 'plop.txt', + } + }, status=400) + assert resp.json.get('err') == 1