diff --git a/api/serializers.py b/api/serializers.py index 1eaf5f3f0..2edcf61bf 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -136,7 +136,25 @@ class Meta: class SourceSerializer(ShareModelSerializer): class Meta: model = models.Source - fields = ('name', 'home_page', 'long_title', 'icon') + fields = '__all__' + + +class SourceConfigSerializer(ShareModelSerializer): + class Meta: + model = models.SourceConfig + fields = '__all__' + + +class HarvesterSerializer(ShareModelSerializer): + class Meta: + model = models.Harvester + fields = '__all__' + + +class TransformerSerializer(ShareModelSerializer): + class Meta: + model = models.Transformer + fields = '__all__' class SiteBannerSerializer(ShareModelSerializer): diff --git a/api/urls.py b/api/urls.py index ec60b1804..145ef4f39 100644 --- a/api/urls.py +++ b/api/urls.py @@ -86,6 +86,9 @@ def register_url(self, subclass, viewset): register_route(r'rawdata', views.RawDatumViewSet) register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) +register_route(r'sourceconfigs', views.SourceConfigViewSet) +register_route(r'harvesters', views.HarvesterViewSet) +register_route(r'transformers', views.TransformerViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') diff --git a/api/views/__init__.py b/api/views/__init__.py index 68bec4096..634d7ec46 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -5,3 +5,4 @@ from .registration import * # noqa from .schema import * # noqa from .banner import * # noqa +from .source_config import * # noqa diff --git a/api/views/source_config.py b/api/views/source_config.py new file mode 100644 index 000000000..3d69be09a --- /dev/null +++ b/api/views/source_config.py @@ -0,0 +1,23 @@ +from api.serializers import SourceConfigSerializer, HarvesterSerializer, TransformerSerializer +from api.views.share import ShareObjectViewSet +from api.pagination import FuzzyPageNumberPagination + +from share.models import SourceConfig, Harvester, Transformer + + +class SourceConfigViewSet(ShareObjectViewSet): + pagination_class = FuzzyPageNumberPagination + serializer_class = SourceConfigSerializer + queryset = SourceConfig.objects.all() + + +class HarvesterViewSet(ShareObjectViewSet): + pagination_class = FuzzyPageNumberPagination + serializer_class = HarvesterSerializer + queryset = Harvester.objects.all() + + +class TransformerViewSet(ShareObjectViewSet): + pagination_class = FuzzyPageNumberPagination + serializer_class = TransformerSerializer + queryset = Transformer.objects.all() diff --git a/api/views/workflow.py b/api/views/workflow.py index 8da559fb3..f1a319ef7 100644 --- a/api/views/workflow.py +++ b/api/views/workflow.py @@ -22,11 +22,13 @@ from api.permissions import ReadOnlyOrTokenHasScopeOrIsAuthenticated from api.serializers import FullNormalizedDataSerializer, BasicNormalizedDataSerializer, \ RawDatumSerializer, ShareUserSerializer, SourceSerializer -from share.models import RawDatum, NormalizedData, Source, SourceConfig, Transformer +from share.models import RawDatum, NormalizedData, Source, SourceConfig, Transformer, ShareUser from share.tasks import disambiguate from share.harvest.serialization import DictSerializer from share.harvest.base import FetchResult from share.util import IDObfuscator +from api.views.share import ShareObjectViewSet +from api.pagination import FuzzyPageNumberPagination logger = logging.getLogger(__name__) __all__ = ('NormalizedDataViewSet', 'RawDatumViewSet', 'ShareUserViewSet', 'SourceViewSet', 'V1DataView') @@ -39,21 +41,22 @@ class ShareUserViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = ShareUserSerializer def get_queryset(self): - return [self.request.user, ] + return ShareUser.objects.filter(pk=self.request.user.pk) -class SourceViewSet(viewsets.ReadOnlyModelViewSet): +class SourceViewSet(ShareObjectViewSet): filter_backends = (filters.OrderingFilter, ) ordering = ('id', ) ordering_fields = ('long_title', ) serializer_class = SourceSerializer permission_classes = [DjangoModelPermissionsOrAnonReadOnly, ] + pagination_class = FuzzyPageNumberPagination queryset = Source.objects.none() # Required for DjangoModelPermissions VALID_IMAGE_TYPES = ('image/png', 'image/jpeg') - def get_queryset(self): + def get_queryset(self, *args): return Source.objects.exclude(icon='').exclude(is_deleted=True) def create(self, request, *args, **kwargs): diff --git a/project/settings.py b/project/settings.py index 918752ed4..19ef0edde 100644 --- a/project/settings.py +++ b/project/settings.py @@ -455,6 +455,7 @@ EMBER_SHARE_PREFIX = os.environ.get('EMBER_SHARE_PREFIX', 'share' if DEBUG else '') EMBER_SHARE_URL = os.environ.get('EMBER_SHARE_URL', 'http://localhost:4200').rstrip('/') + '/' SHARE_API_URL = os.environ.get('SHARE_API_URL', 'http://localhost:8000').rstrip('/') + '/' +SHARE_API_URL_PRODUCTION = os.environ.get('SHARE_API_URL_PRODUCTION', 'https://share.osf.io/api/v2').rstrip('/') + '/' SHARE_WEB_URL = os.environ.get('SHARE_WEB_URL', SHARE_API_URL + EMBER_SHARE_PREFIX).rstrip('/') + '/' SHARE_USER_AGENT = os.environ.get('SHARE_USER_AGENT', 'SHAREbot/{} (+{})'.format(VERSION, SHARE_WEB_URL)) diff --git a/tests/api/test_sources_endpoint.py b/tests/api/test_sources_endpoint.py index 0b372575c..93c5384cd 100644 --- a/tests/api/test_sources_endpoint.py +++ b/tests/api/test_sources_endpoint.py @@ -3,6 +3,7 @@ import time import httpretty +from furl import furl from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType @@ -18,8 +19,8 @@ def exceptionCallback(request, uri, headers): - time.sleep(6) - return (400, headers, uri) + time.sleep(6) + return (400, headers, uri) @pytest.fixture @@ -110,6 +111,12 @@ def test_no_icon(self, client): assert resp.status_code == 200 assert resp.json()['meta']['pagination']['count'] == total - 1 + def test_get_object(self, client): + s = Source.objects.first() + resp = client.get(furl(self.endpoint).join(IDObfuscator.encode_id(s.pk, Source) + '/').url) + assert resp.status_code == 200 + assert IDObfuscator.decode_id(resp.json()['data']['id']) == s.pk + @pytest.mark.django_db class TestSourcesPost: diff --git a/tests/api/test_user_endpoint.py b/tests/api/test_user_endpoint.py new file mode 100644 index 000000000..ac4b1cb29 --- /dev/null +++ b/tests/api/test_user_endpoint.py @@ -0,0 +1,49 @@ +import pytest +import json + + +@pytest.fixture +def post_body_share_user(): + return json.dumps({ + 'data': { + 'type': 'ShareUser', + 'attributes': { + 'username': 'TestUser' + } + } + }) + + +@pytest.mark.django_db +class TestSourcesGet: + endpoint = '/api/v2/user/' + + def test_logged_in(self, client, share_user): + resp = client.get(self.endpoint, HTTP_AUTHORIZATION=share_user.authorization()) + assert resp.status_code == 200 + assert resp.json()['meta']['pagination']['count'] == 1 + + def test_not_logged_in(self, client): + resp = client.get(self.endpoint) + assert resp.status_code == 200 + assert resp.json()['meta']['pagination']['count'] == 0 + + +@pytest.mark.django_db +class TestSourcesPost: + endpoint = '/api/v2/user/' + + def test_unauthorized_post(self, client, post_body_share_user): + assert client.post( + self.endpoint, + post_body_share_user, + content_type='application/vnd.api+json' + ).status_code == 401 + + def test_authorized_post(self, client, share_user, post_body_share_user): + assert client.post( + self.endpoint, + post_body_share_user, + content_type='application/vnd.api+json', + HTTP_AUTHORIZATION=share_user.authorization(), + ).status_code == 405