diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 797276e8a3aec0ead1d0514e4d26ae46abb7ca35..15aa90907fc51bd7a34d6d44486459b9547cd85b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,29 +2,21 @@ image: python:3.6 stages: - #- test + - test - release -#test: -# stage: test -# script: -# - echo 'Make your tests here !' -# except: -# - master -# tags: -# - sib +include: + - project: 'infra/platform' + ref: master + file: '/templates/python.ci.yml' -publish: - stage: release - before_script: - - pip install python-semantic-release~=5.0 sib-commit-parser~=0.3 - - git config user.name "${GITLAB_USER_NAME}" - - git config user.email "${GITLAB_USER_EMAIL}" - - git remote set-url origin "https://gitlab-ci-token:${GL_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" - - git fetch --tags +test: + stage: test script: - - semantic-release publish - only: + - pip install .[dev] + - python -m unittest coopstarter_data.tests.runner + except: - master + - tags tags: - - deploy + - test diff --git a/coopstarter_data/filters.py b/coopstarter_data/filters.py new file mode 100644 index 0000000000000000000000000000000000000000..18937d81350f77efcc68a49a7f2e4a51e9f1c122 --- /dev/null +++ b/coopstarter_data/filters.py @@ -0,0 +1,11 @@ +from rest_framework.filters import BaseFilterBackend + + +class ValidatedResourcesByStepFilterBackend(BaseFilterBackend): + def filter_queryset(self, request, queryset, view): + return queryset.filter(steps__in=view.kwargs['id'], review__status='validated') + + +class PendingResourceFilterBackend(BaseFilterBackend): + def filter_queryset(self, request, queryset, view): + return queryset.filter(review__status='pending').exclude(submitter__urlid=request.user.urlid) diff --git a/coopstarter_data/models.py b/coopstarter_data/models.py index bebf8187f7a46e246c1ff76fafde47e46e832e59..88ffd21a80aae30294d6069daa0f4c0900b6a159 100644 --- a/coopstarter_data/models.py +++ b/coopstarter_data/models.py @@ -373,7 +373,7 @@ def update_review(sender, instance, created, **kwargs): return False if not created: - if instance.resource: + if getattr(settings, 'UPDATE_REVIEW', True) and instance.resource is not None: resource = instance.resource if instance.status == 'validated': subject = 'The resource you submitted has been validated !' diff --git a/coopstarter_data/permissions.py b/coopstarter_data/permissions.py new file mode 100644 index 0000000000000000000000000000000000000000..8f6bb76ff1244ccff5bf07d6eb99e039faa0df08 --- /dev/null +++ b/coopstarter_data/permissions.py @@ -0,0 +1,14 @@ +from djangoldp.permissions import LDPPermissions + + +class PendingResourcePermissions(LDPPermissions): + anonymous_perms = [] + authenticated_perms = ['view', 'add', 'delete', 'change'] + + def has_permission(self, request, view): + if request.method == 'OPTIONS': + return True + + if request.user.is_anonymous: + return False + return True diff --git a/coopstarter_data/tests/__init__.py b/coopstarter_data/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/coopstarter_data/tests/__pycache__/__init__.cpython-36.pyc b/coopstarter_data/tests/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b183c313e8545070cee421c2413ae33851b6810 Binary files /dev/null and b/coopstarter_data/tests/__pycache__/__init__.cpython-36.pyc differ diff --git a/coopstarter_data/tests/__pycache__/models.cpython-36.pyc b/coopstarter_data/tests/__pycache__/models.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7fe8317744881adf2ba038a7f520b08bb329115 Binary files /dev/null and b/coopstarter_data/tests/__pycache__/models.cpython-36.pyc differ diff --git a/coopstarter_data/tests/__pycache__/runner.cpython-36.pyc b/coopstarter_data/tests/__pycache__/runner.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b535aaff5ad529b9700d7f36d321b1e7ddb7eff Binary files /dev/null and b/coopstarter_data/tests/__pycache__/runner.cpython-36.pyc differ diff --git a/coopstarter_data/tests/__pycache__/tests_get.cpython-36.pyc b/coopstarter_data/tests/__pycache__/tests_get.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5e952653a82c30b0fb2e1d4410f449a0d326c31 Binary files /dev/null and b/coopstarter_data/tests/__pycache__/tests_get.cpython-36.pyc differ diff --git a/coopstarter_data/tests/models.py b/coopstarter_data/tests/models.py new file mode 100644 index 0000000000000000000000000000000000000000..0f0400b1817724563f8a447476410a4c14231ce5 --- /dev/null +++ b/coopstarter_data/tests/models.py @@ -0,0 +1,13 @@ +from django.contrib.auth.models import AbstractUser + +from djangoldp.models import Model + + +class User(AbstractUser, Model): + + class Meta(AbstractUser.Meta, Model.Meta): + serializer_fields = ['@id', 'username', 'first_name', 'last_name', 'email', 'resources'] + nested_fields = ['resources'] + anonymous_perms = ['view', 'add'] + authenticated_perms = ['inherit', 'change'] + owner_perms = ['inherit'] diff --git a/coopstarter_data/tests/runner.py b/coopstarter_data/tests/runner.py new file mode 100644 index 0000000000000000000000000000000000000000..5b98333f1f3540092d8fe38f80b044773eefc735 --- /dev/null +++ b/coopstarter_data/tests/runner.py @@ -0,0 +1,46 @@ +import sys + +import django +from django.conf import settings +from djangoldp.tests import settings_default + +settings.configure(default_settings=settings_default, + DJANGOLDP_PACKAGES=['djangoldp_conversation', 'djangoldp_like', 'coopstarter_data', + 'coopstarter_data.tests', ], + INSTALLED_APPS=('django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.admin', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'guardian', + 'djangoldp_conversation', + 'djangoldp_like', + 'coopstarter_data', + 'coopstarter_data.tests', + 'djangoldp', + ), + SITE_URL='http://happy-dev.fr', + BASE_URL='http://happy-dev.fr', + REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'djangoldp.pagination.LDPPagination', + 'PAGE_SIZE': 5 + }, + SEND_BACKLINKS=False, + JABBER_DEFAULT_HOST=None, + PERMISSIONS_CACHE=False, + ANONYMOUS_USER_NAME=None, + SERIALIZER_CACHE=False, + UPDATE_REVIEW=False + ) + +django.setup() +from django.test.runner import DiscoverRunner + +test_runner = DiscoverRunner(verbosity=1) + +failures = test_runner.run_tests([ + 'coopstarter_data.tests.tests_get', +]) +if failures: + sys.exit(failures) diff --git a/coopstarter_data/tests/tests_get.py b/coopstarter_data/tests/tests_get.py new file mode 100644 index 0000000000000000000000000000000000000000..a0cbbd1f29a464328bea258a0e4ab2fa757988d6 --- /dev/null +++ b/coopstarter_data/tests/tests_get.py @@ -0,0 +1,115 @@ +import uuid +import json +from datetime import datetime, timedelta + +from djangoldp.permissions import LDPPermissions + +from djangoldp.serializers import LDListMixin, LDPSerializer +from rest_framework.test import APITestCase, APIClient + +from coopstarter_data.models import Resource, Review, Step +from coopstarter_data.tests.models import User + + +class GETTestCase(APITestCase): + def setUp(self): + self.client = APIClient() + LDListMixin.to_representation_cache.reset() + LDPSerializer.to_representation_cache.reset() + LDPPermissions.invalidate_cache() + + def setUpLoggedInUser(self): + self.user = User(email='test@mactest.co.uk', first_name='Test', last_name='Mactest', username='test', + password='glass onion') + self.user.save() + self.client.force_authenticate(user=self.user) + + def setUpResource(self): + self.resource = self._get_resource(name='Test') + + def _get_attached_review(self, resource=None, **extra): + if resource is None: + resource = self.resource + + review = Review.objects.create(**extra) + resource.review = review + resource.save() + return review + + def _get_resource(self, **extra): + return Resource.objects.create(**extra) + + def _get_step(self): + return Step.objects.create(name='Test') + + def test_list_resources_nested_serializer(self): + self.setUpLoggedInUser() + self.resource = self._get_resource(name='Test', submitter=self.user) + + response = self.client.get('/users/1/') + self.assertIn('resources', response.data) + self.assertEqual(len(response.data['resources']['ldp:contains']), 1) + self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'], self.resource.urlid) + + def test_list_resources_nested_viewset(self): + self.setUpLoggedInUser() + self.resource = self._get_resource(name='Test', submitter=self.user) + + response = self.client.get('/users/1/resources/') + self.assertIn('ldp:contains', response.data) + self.assertEqual(len(response.data['ldp:contains']), 1) + self.assertEqual(response.data['ldp:contains'][0]['@id'], self.resource.urlid) + + def test_list_pending_resources(self): + self.setUpLoggedInUser() + # one local resource which nobody submitted + self.setUpResource() + self._get_attached_review(resource=self.resource, status='pending') + + # two external resources (should not be returned) + external_resource_backlink = self._get_resource(name='Test2', is_backlink=True, urlid='https://external.com/resource/1/') + self._get_attached_review(resource=external_resource_backlink, status='pending') + external_resource_non_backlink = self._get_resource(name='Test3', is_backlink=False, urlid='https://external.com/resource/2/') + self._get_attached_review(resource=external_resource_non_backlink, status='pending') + + # one local resource which I did submit (should not be returned) + resource = self._get_resource(name='Test4', submitter=self.user) + self._get_attached_review(resource=resource, status='pending') + + response = self.client.get('/resources/pending/') + self.assertIn('ldp:contains', response.data) + self.assertEqual(len(response.data['ldp:contains']), 1) + self.assertEqual(response.data['ldp:contains'][0]['name'], 'Test') + + def test_list_pending_resources_anonymous(self): + self.setUpResource() + self._get_attached_review(resource=self.resource, status='pending') + + response = self.client.get('/resources/pending/') + self.assertEqual(response.status_code, 403) + + def test_list_pending_resources_options_anonymous(self): + self.setUpResource() + self._get_attached_review(resource=self.resource, status='pending') + + response = self.client.options('/resources/pending/') + self.assertEqual(response.status_code, 200) + + def test_list_validated_resources(self): + step = self._get_step() + self.setUpResource() + self._get_attached_review(resource=self.resource, status='validated') + self.resource.steps.add(step) + + pending_resource = self._get_resource() + self._get_attached_review(resource=pending_resource, status='pending') + pending_resource.steps.add(step) + + external_resource = self._get_resource(urlid='https://external.com/resource/1/') + self._get_attached_review(resource=external_resource, status='validated') + external_resource.steps.add(step) + + response = self.client.get('/steps/{}/resources/validated/'.format(step.pk)) + self.assertIn('ldp:contains', response.data) + self.assertEqual(len(response.data['ldp:contains']), 1) + self.assertEqual(response.data['ldp:contains'][0]['name'], 'Test') diff --git a/coopstarter_data/views.py b/coopstarter_data/views.py index 44717bae2f7553034ca85a994ec06e70a7ea66f3..ea8b1ff0448bfcbb723c61af29ecb0e5e727021b 100644 --- a/coopstarter_data/views.py +++ b/coopstarter_data/views.py @@ -1,31 +1,16 @@ -from djangoldp.views import LDPViewSet -from .models import Resource, Step +from djangoldp.filters import LocalObjectFilterBackend +from coopstarter_data.models import Resource +from coopstarter_data.filters import PendingResourceFilterBackend, ValidatedResourcesByStepFilterBackend +from coopstarter_data.permissions import PendingResourcePermissions from djangoldp_i18n.views import I18nLDPViewSet -class ValidatedResourcesByStepViewSet(I18nLDPViewSet): - model = Resource - def get_queryset(self, *args, **kwargs): - step_id = self.kwargs['id'] - # if hasattr(self.request.user, 'contributor_profile'): - # target='contributor' - # elif hasattr(self.request.user, 'searcher_profile'): - # target='searcher' - # else: - # target='public' +class ValidatedResourcesByStepViewSet(I18nLDPViewSet): + model = Resource + filter_backends = [ValidatedResourcesByStepFilterBackend, LocalObjectFilterBackend] - # Additional filter criteria: , target__value=target - return super().get_queryset(*args, **kwargs)\ - .filter(steps__in=step_id, review__status='validated')\ - .exclude(is_backlink=True) class PendingResourcesViewSet(I18nLDPViewSet): - model = Resource - - def get_queryset(self, *args, **kwargs): - # Deactivating those additional filters for now. - # , language__in=self.request.user.contributor_profile.languages.all(), fields__in=self.request.user.contributor_profile.fields.all() - return super().get_queryset(*args, **kwargs)\ - .filter(review__status='pending')\ - .exclude(submitter__username=self.request.user.username)\ - .exclude(is_backlink=True) \ No newline at end of file + model = Resource + filter_backends = [PendingResourceFilterBackend, LocalObjectFilterBackend] + permission_classes = [PendingResourcePermissions] diff --git a/setup.cfg b/setup.cfg index 245ae45223a270f5e90a3bc53c6a5c8d5e2fac73..94765136fc3b104282438e9784dde933bed8a248 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,15 @@ classifiers = [options] packages = find: install_requires = - djangoldp~=0.5 + djangoldp>=1.4 + djangoldp_account>=1.0.0 + djangoldp_conversation>=1.0.0 + djangoldp_like>=0.0.1 + djangoldp_i18n>=1.0.0 + +[options.extras_require] +dev = + factory_boy>=2.11.0 [semantic_release] version_source = tag diff --git a/setup.py b/setup.py index 6b76fe5111ad4f555c279456efbc252bd4024ac9..de90db022d444a5702afc118f35d51a2ebb19961 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,5 @@ from setuptools import setup setup( - include_package_data=True, - install_requires=["Pillow"] + include_package_data=True )