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)
|
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
|
||||||
path = models.CharField(max_length=512, default="")
|
path = models.CharField(max_length=512, default="")
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
last_checked = models.DateTimeField()
|
last_checked = models.DateTimeField(null=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
@ -27,9 +27,14 @@ class CustomDecimalField(serializers.Field):
|
|||||||
|
|
||||||
|
|
||||||
class UserNameSerializer(serializers.ModelSerializer):
|
class UserNameSerializer(serializers.ModelSerializer):
|
||||||
|
username = serializers.SerializerMethodField('get_user_label')
|
||||||
|
|
||||||
|
def get_user_label(self, obj):
|
||||||
|
return obj.get_user_name()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('id', 'username', 'first_name', 'last_name')
|
fields = ('id', 'username')
|
||||||
|
|
||||||
|
|
||||||
class UserPreferenceSerializer(serializers.ModelSerializer):
|
class UserPreferenceSerializer(serializers.ModelSerializer):
|
||||||
@ -42,7 +47,12 @@ class UserPreferenceSerializer(serializers.ModelSerializer):
|
|||||||
class StorageSerializer(serializers.ModelSerializer):
|
class StorageSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Storage
|
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):
|
class SyncSerializer(serializers.ModelSerializer):
|
||||||
|
@ -470,11 +470,7 @@
|
|||||||
updateUserNames: function () {
|
updateUserNames: function () {
|
||||||
return this.$http.get("{% url 'api:username-list' %}?filter_list=[" + this.user_id_update + ']').then((response) => {
|
return this.$http.get("{% url 'api:username-list' %}?filter_list=[" + this.user_id_update + ']').then((response) => {
|
||||||
for (let u of response.data) {
|
for (let u of response.data) {
|
||||||
let name = u.username
|
this.$set(this.user_names, u.id, u.username);
|
||||||
if (`${u.first_name} ${u.last_name}` !== ' ') {
|
|
||||||
name = `${u.first_name} ${u.last_name}`
|
|
||||||
}
|
|
||||||
this.$set(this.user_names, u.id, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch((err) => {
|
}).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
|
from cookbook.helper import dal
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
|
router.register(r'user-name', api.UserNameViewSet, basename='username')
|
||||||
router.register(r'user-preference', api.UserPreferenceViewSet)
|
router.register(r'user-preference', api.UserPreferenceViewSet)
|
||||||
|
router.register(r'storage', api.StorageViewSet)
|
||||||
|
|
||||||
router.register(r'unit', api.UnitViewSet)
|
router.register(r'unit', api.UnitViewSet)
|
||||||
router.register(r'food', api.FoodViewSet)
|
router.register(r'food', api.FoodViewSet)
|
||||||
router.register(r'step', api.StepViewSet)
|
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-plan', api.MealPlanViewSet)
|
||||||
router.register(r'meal-type', api.MealTypeViewSet)
|
router.register(r'meal-type', api.MealTypeViewSet)
|
||||||
router.register(r'view-log', api.ViewLogViewSet)
|
router.register(r'view-log', api.ViewLogViewSet)
|
||||||
router.register(r'user-name', api.UserNameViewSet, basename='username')
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.index, name='index'),
|
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.parsers import JSONParser, FileUploadParser, MultiPartParser
|
||||||
from rest_framework.response import Response
|
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.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.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.dropbox import Dropbox
|
||||||
from cookbook.provider.nextcloud import Nextcloud
|
from cookbook.provider.nextcloud import Nextcloud
|
||||||
from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \
|
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:
|
list:
|
||||||
optional parameters
|
optional parameters
|
||||||
@ -39,11 +39,11 @@ class UserNameViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserNameSerializer
|
serializer_class = UserNameSerializer
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [CustomIsGuest]
|
||||||
http_method_names = ['get']
|
http_method_names = ['get']
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = User.objects.all()
|
queryset = self.queryset
|
||||||
try:
|
try:
|
||||||
filter_list = self.request.query_params.get('filter_list', None)
|
filter_list = self.request.query_params.get('filter_list', None)
|
||||||
if filter_list is not None:
|
if filter_list is not None:
|
||||||
@ -70,6 +70,13 @@ class UserPreferenceViewSet(viewsets.ModelViewSet):
|
|||||||
return self.queryset.filter(user=self.request.user)
|
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):
|
class RecipeBookViewSet(RetrieveModelMixin, UpdateModelMixin, ListModelMixin, viewsets.GenericViewSet):
|
||||||
queryset = RecipeBook.objects.all()
|
queryset = RecipeBook.objects.all()
|
||||||
serializer_class = RecipeBookSerializer
|
serializer_class = RecipeBookSerializer
|
||||||
|
Loading…
Reference in New Issue
Block a user