added api endpoints and tests
This commit is contained in:
parent
692811f84f
commit
e83565b1f2
18
cookbook/migrations/0073_auto_20200708_2311.py
Normal file
18
cookbook/migrations/0073_auto_20200708_2311.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.0.7 on 2020-07-08 21:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0072_step_show_as_header'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='sync',
|
||||
name='last_checked',
|
||||
field=models.DateTimeField(null=True),
|
||||
),
|
||||
]
|
@ -93,7 +93,7 @@ class Sync(models.Model):
|
||||
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
||||
path = models.CharField(max_length=512, default="")
|
||||
active = models.BooleanField(default=True)
|
||||
last_checked = models.DateTimeField()
|
||||
last_checked = models.DateTimeField(null=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
@ -27,9 +27,14 @@ class CustomDecimalField(serializers.Field):
|
||||
|
||||
|
||||
class UserNameSerializer(serializers.ModelSerializer):
|
||||
username = serializers.SerializerMethodField('get_user_label')
|
||||
|
||||
def get_user_label(self, obj):
|
||||
return obj.get_user_name()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('id', 'username', 'first_name', 'last_name')
|
||||
fields = ('id', 'username')
|
||||
|
||||
|
||||
class UserPreferenceSerializer(serializers.ModelSerializer):
|
||||
@ -42,7 +47,12 @@ class UserPreferenceSerializer(serializers.ModelSerializer):
|
||||
class StorageSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Storage
|
||||
fields = ('id', 'name', 'method', 'username', 'created_by')
|
||||
fields = ('id', 'name', 'method', 'username', 'password', 'token', 'created_by')
|
||||
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': True},
|
||||
'token': {'write_only': True},
|
||||
}
|
||||
|
||||
|
||||
class SyncSerializer(serializers.ModelSerializer):
|
||||
|
@ -470,11 +470,7 @@
|
||||
updateUserNames: function () {
|
||||
return this.$http.get("{% url 'api:username-list' %}?filter_list=[" + this.user_id_update + ']').then((response) => {
|
||||
for (let u of response.data) {
|
||||
let name = u.username
|
||||
if (`${u.first_name} ${u.last_name}` !== ' ') {
|
||||
name = `${u.first_name} ${u.last_name}`
|
||||
}
|
||||
this.$set(this.user_names, u.id, name);
|
||||
this.$set(this.user_names, u.id, u.username);
|
||||
}
|
||||
|
||||
}).catch((err) => {
|
||||
|
61
cookbook/tests/api/test_api_storage.py
Normal file
61
cookbook/tests/api/test_api_storage.py
Normal file
@ -0,0 +1,61 @@
|
||||
import json
|
||||
|
||||
from django.contrib import auth
|
||||
from django.db.models import ProtectedError
|
||||
from django.urls import reverse
|
||||
|
||||
from cookbook.models import Storage, Sync
|
||||
from cookbook.tests.views.test_views import TestViews
|
||||
|
||||
|
||||
class TestApiStorage(TestViews):
|
||||
|
||||
def setUp(self):
|
||||
super(TestApiStorage, self).setUp()
|
||||
self.storage = Storage.objects.create(
|
||||
name='Test Storage',
|
||||
username='test',
|
||||
password='password',
|
||||
token='token',
|
||||
url='url',
|
||||
created_by=auth.get_user(self.admin_client_1)
|
||||
)
|
||||
|
||||
def test_storage_list(self):
|
||||
# verify view permissions are applied accordingly
|
||||
self.batch_requests([(self.anonymous_client, 403), (self.guest_client_1, 403), (self.user_client_1, 403), (self.admin_client_1, 200), (self.superuser_client, 200)],
|
||||
reverse('api:storage-list'))
|
||||
|
||||
# verify storage is returned
|
||||
r = self.admin_client_1.get(reverse('api:storage-list'))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
response = json.loads(r.content)
|
||||
self.assertEqual(len(response), 1)
|
||||
storage_response = response[0]
|
||||
self.assertEqual(storage_response['name'], self.storage.name)
|
||||
self.assertFalse('password' in storage_response)
|
||||
self.assertFalse('token' in storage_response)
|
||||
|
||||
def test_storage_update(self):
|
||||
# can update storage as admin
|
||||
r = self.admin_client_1.patch(reverse('api:storage-detail', args={self.storage.id}), {'name': 'new', 'password': 'new_password'}, content_type='application/json')
|
||||
response = json.loads(r.content)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(response['name'], 'new')
|
||||
|
||||
# verify password was updated (write only field)
|
||||
self.storage.refresh_from_db()
|
||||
self.assertEqual(self.storage.password, 'new_password')
|
||||
|
||||
def test_storage_delete(self):
|
||||
# can delete storage as admin
|
||||
r = self.admin_client_1.delete(reverse('api:storage-detail', args={self.storage.id}))
|
||||
self.assertEqual(r.status_code, 204)
|
||||
self.assertEqual(Storage.objects.count(), 0)
|
||||
|
||||
self.storage = Storage.objects.create(created_by=auth.get_user(self.admin_client_1), name='test protect')
|
||||
Sync.objects.create(storage=self.storage, )
|
||||
|
||||
# test if deleting a storage with existing sync fails (as sync protects storage)
|
||||
with self.assertRaises(ProtectedError):
|
||||
self.admin_client_1.delete(reverse('api:storage-detail', args={self.storage.id}))
|
27
cookbook/tests/api/test_api_username.py
Normal file
27
cookbook/tests/api/test_api_username.py
Normal file
@ -0,0 +1,27 @@
|
||||
import json
|
||||
|
||||
from django.contrib import auth
|
||||
from django.urls import reverse
|
||||
|
||||
from cookbook.models import UserPreference
|
||||
from cookbook.tests.views.test_views import TestViews
|
||||
|
||||
|
||||
class TestApiUsername(TestViews):
|
||||
|
||||
def setUp(self):
|
||||
super(TestApiUsername, self).setUp()
|
||||
|
||||
def test_forbidden_methods(self):
|
||||
r = self.user_client_1.post(reverse('api:username-list'))
|
||||
self.assertEqual(r.status_code, 405)
|
||||
|
||||
r = self.user_client_1.put(reverse('api:username-detail', args=[auth.get_user(self.user_client_1).pk]))
|
||||
self.assertEqual(r.status_code, 405)
|
||||
|
||||
r = self.user_client_1.delete(reverse('api:username-detail', args=[auth.get_user(self.user_client_1).pk]))
|
||||
self.assertEqual(r.status_code, 405)
|
||||
|
||||
def test_username_list(self):
|
||||
self.batch_requests([(self.anonymous_client, 403), (self.guest_client_1, 200), (self.user_client_1, 200), (self.admin_client_1, 200), (self.superuser_client, 200)],
|
||||
reverse('api:username-list'))
|
@ -9,7 +9,10 @@ from cookbook.views import api, import_export
|
||||
from cookbook.helper import dal
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'user-name', api.UserNameViewSet, basename='username')
|
||||
router.register(r'user-preference', api.UserPreferenceViewSet)
|
||||
router.register(r'storage', api.StorageViewSet)
|
||||
|
||||
router.register(r'unit', api.UnitViewSet)
|
||||
router.register(r'food', api.FoodViewSet)
|
||||
router.register(r'step', api.StepViewSet)
|
||||
@ -19,7 +22,7 @@ router.register(r'ingredient', api.IngredientViewSet)
|
||||
router.register(r'meal-plan', api.MealPlanViewSet)
|
||||
router.register(r'meal-type', api.MealTypeViewSet)
|
||||
router.register(r'view-log', api.ViewLogViewSet)
|
||||
router.register(r'user-name', api.UserNameViewSet, basename='username')
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.index, name='index'),
|
||||
|
@ -21,16 +21,16 @@ from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, ListMode
|
||||
from rest_framework.parsers import JSONParser, FileUploadParser, MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
|
||||
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser
|
||||
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser, CustomIsGuest
|
||||
from cookbook.helper.recipe_url_import import get_from_html
|
||||
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \
|
||||
KeywordSerializer, RecipeImageSerializer
|
||||
KeywordSerializer, RecipeImageSerializer, StorageSerializer
|
||||
|
||||
|
||||
class UserNameViewSet(viewsets.ModelViewSet):
|
||||
class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
list:
|
||||
optional parameters
|
||||
@ -39,11 +39,11 @@ class UserNameViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserNameSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
permission_classes = [CustomIsGuest]
|
||||
http_method_names = ['get']
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = User.objects.all()
|
||||
queryset = self.queryset
|
||||
try:
|
||||
filter_list = self.request.query_params.get('filter_list', None)
|
||||
if filter_list is not None:
|
||||
@ -70,6 +70,13 @@ class UserPreferenceViewSet(viewsets.ModelViewSet):
|
||||
return self.queryset.filter(user=self.request.user)
|
||||
|
||||
|
||||
class StorageViewSet(viewsets.ModelViewSet):
|
||||
# TODO handle delete protect error and adjust test
|
||||
queryset = Storage.objects.all()
|
||||
serializer_class = StorageSerializer
|
||||
permission_classes = [CustomIsAdmin, ]
|
||||
|
||||
|
||||
class RecipeBookViewSet(RetrieveModelMixin, UpdateModelMixin, ListModelMixin, viewsets.GenericViewSet):
|
||||
queryset = RecipeBook.objects.all()
|
||||
serializer_class = RecipeBookSerializer
|
||||
|
Loading…
Reference in New Issue
Block a user