From e63ce9451dc17fe5fd7cc7e9a49e6da3b47dca19 Mon Sep 17 00:00:00 2001 From: Kacha Mukabe Date: Tue, 7 Jan 2020 09:13:46 +0200 Subject: [PATCH] GraphQL solution --- app/api/admin.py | 7 +- app/api/apps.py | 5 + app/api/models.py | 80 ++++-------- app/api/schema.py | 143 +++++++++++++++++++++ app/api/serializers.py | 34 ----- app/api/tests.py | 285 ----------------------------------------- app/api/urls.py | 16 --- app/api/views.py | 157 ----------------------- app/app/schema.py | 16 +++ app/app/settings.py | 48 ++++--- app/app/urls.py | 14 +- app/requirements.txt | 26 +++- 12 files changed, 255 insertions(+), 576 deletions(-) create mode 100644 app/api/apps.py create mode 100644 app/api/schema.py delete mode 100644 app/api/serializers.py create mode 100644 app/app/schema.py diff --git a/app/api/admin.py b/app/api/admin.py index 31a0bdc..f194189 100644 --- a/app/api/admin.py +++ b/app/api/admin.py @@ -1,6 +1,5 @@ from django.contrib import admin +from api.models import UserProfile, Followers -# Register your models here. -from .models import Users - -admin.site.register(Users) \ No newline at end of file +admin.site.register(UserProfile) +admin.site.register(Followers) \ No newline at end of file diff --git a/app/api/apps.py b/app/api/apps.py new file mode 100644 index 0000000..d87006d --- /dev/null +++ b/app/api/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'api' diff --git a/app/api/models.py b/app/api/models.py index 928baf8..b31e437 100644 --- a/app/api/models.py +++ b/app/api/models.py @@ -1,10 +1,12 @@ from django.db import models +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.conf import settings -# Create your models here. -class Users(models.Model): - username = models.CharField(max_length=100) - password = models.CharField(max_length=100) - email = models.CharField(max_length=100, unique=True) + +class UserProfile(models.Model): + user = models.OneToOneField(User,on_delete=models.CASCADE,related_name="profile") birthday = models.DateField(auto_now=False, auto_now_add=False, null=True) company = models.CharField(max_length=50, null=True) location = models.CharField(max_length=50, null=True) @@ -12,64 +14,32 @@ class Users(models.Model): updated_at = models.DateField(auto_now=True) def __str__(self): - return '{} {} {} {} {}'.format(self.username, self.email, self.birthday, self.company, self.location) + return '{} {} {} {} {} {}'.format(self.id,self.user.username, self.user.email, self.birthday, self.company, self.location) + + +@receiver(post_save, sender=User) +def create_user_profile(sender, instance, created, **kwargs): + if created: + UserProfile.objects.create(user=instance) + + +# @receiver(post_save, sender=User) +# def save_user_profile(sender, instance, **kwargs): +# instance.profile.save() -# from passlib.hash import pbkdf2_sha256 -# hash = pbkdf2_sha256.encrypt("password", rounds=8000, salt_size=10) -# hash = $pbkdf2-sha256$8000$HaPUeu99L4VQKg$TmNh1N7/aQkkfVE1902sHc0uBHzwuVEUrMW3q5oiZwg -# u = Users(username="user1", password=hash,email="user1@test.com",birthday="1991-04-17",company="company1",location="location1") -# u.save() -# u = Users(username="user2", password=hash,email="user2@test.com",birthday="1989-04-17",company="company2",location="location2") -# u.save() -# u = Users(username="user3", password=hash,email="user3@test.com",birthday="1995-04-17",company="company3",location="location1") -# u.save() -# u = Users(username="user4", password=hash,email="user4@test.com",birthday="1987-04-17",company="company2",location="location1") -# u.save() -# u = Users(username="user5", password=hash,email="user5@test.com",birthday="1999-04-17",company="company2",location="location2") -# u.save() -# u = Users(username="user6", password=hash,email="user6@test.com",birthday="1994-04-17",company="company1",location="location2") -# u.save() class Followers(models.Model): - user_id = models.ForeignKey(Users, on_delete=models.CASCADE, related_name='followers_user_followed') # User who is followed - followed_by_id = models.ForeignKey(Users, on_delete=models.CASCADE, related_name='followers_follower') # User one who following + user_id = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='followers_user_followed') # User who is followed + followed_by_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, + related_name='followers_follower') # User one who following followed_at = models.DateField(auto_now_add=True) - + class Meta: unique_together = ('user_id', 'followed_by_id') def __str__(self): - return '{} {} {} {}'.format(self.id, self.user_id.id, self.followed_by_id.id, self.followed_at) - -# user = Users.objects.get(id=1) -# follower = Users.objects.get(id=2) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() -# user = Users.objects.get(id=1) -# follower = Users.objects.get(id=3) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() -# user = Users.objects.get(id=1) -# follower = Users.objects.get(id=4) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() -# user = Users.objects.get(id=1) -# follower = Users.objects.get(id=5) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() -# user = Users.objects.get(id=1) -# follower = Users.objects.get(id=6) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() -# user = Users.objects.get(id=2) -# follower = Users.objects.get(id=1) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() -# user = Users.objects.get(id=2) -# follower = Users.objects.get(id=3) -# f = Followers(user_id=user, followed_by_id=follower) -# f.save() - + return '{} {} {} {}'.format(self.id, self.user_id.id, self.followed_by_id, self.followed_at) diff --git a/app/api/schema.py b/app/api/schema.py new file mode 100644 index 0000000..d941ba1 --- /dev/null +++ b/app/api/schema.py @@ -0,0 +1,143 @@ +import graphene +from django.contrib.auth import get_user_model +from graphene_django.types import DjangoObjectType +from .models import Followers, UserProfile + + +class UserType(DjangoObjectType): + class Meta: + model = get_user_model() + + +class ProfileType(DjangoObjectType): + class Meta: + model = UserProfile + + +class FollowerType(DjangoObjectType): + class Meta: + model = Followers + + +class CreateUser(graphene.Mutation): + user = graphene.Field(UserType) + + class Arguments: + username = graphene.String(required=True) + password = graphene.String(required=True) + email = graphene.String(required=True) + + def mutate(self, info, username, password, email): + user = get_user_model()( + username=username, + email=email, + ) + user.set_password(password) + user.save() + return CreateUser(user=user) + + +class CreateFollower(graphene.Mutation): + id = graphene.Int() + user_id = graphene.Field(UserType) + followed_by_id = graphene.Field(UserType) + followed_at = graphene.DateTime() + + class Arguments: + user_id = graphene.Int(required=True) + + def mutate(self, info, user_id): + user = info.context.user or None + + follower = Followers( + user_id_id=user_id, + followed_by_id=user + ) + + follower.save() + + return CreateFollower( + id=follower.id, + user_id=follower.user_id, + followed_by_id=follower.followed_by_id, + followed_at=follower.followed_at + ) + + +class EditProfile(graphene.Mutation): + location = graphene.String() + company = graphene.String() + birthday = graphene.Date() + profile = graphene.Field(ProfileType) + + class Arguments: + location = graphene.String(required=True) + company = graphene.String(required=True) + birthday = graphene.Date(required=False) + + def mutate(self, info, location, company, birthday): + user = info.context.user or None + print(user, user.id) + print(UserProfile.objects.all()) + profile = UserProfile.objects.get(user_id=user.id) + profile.company = company + profile.location = location + profile.birthday = birthday + + profile.save() + + return EditProfile( + company = profile.company, + location = profile.location, + birthday = profile.birthday + ) + + +class DeleteFollower(graphene.Mutation): + ok = graphene.Boolean() + + class Arguments: + id = graphene.Int(required=True) + + def mutate(self, info, id): + follower = Followers.objects.get(pk=id) + follower.delete() + return DeleteFollower( + ok=True + ) + + +class Mutation(graphene.ObjectType): + create_user = CreateUser.Field() + create_follower = CreateFollower.Field() + edit_profile = EditProfile.Field() + delete_follower = DeleteFollower.Field() + + +class Query(object): + user = graphene.Field(UserType, id=graphene.Int(), name=graphene.String()) + all_users = graphene.List(UserType) + follower = graphene.Field(FollowerType, user_id=graphene.Int()) + all_followers = graphene.List(FollowerType) + all_following = graphene.List(FollowerType) + all_profiles = graphene.List(ProfileType) + + def resolve_all_users(self, info, **kwargs): + return get_user_model().objects.all() + + def resolve_all_profiles(self,info, **kwargs): + return UserProfile.objects.all() + + def resolve_all_following(self,info, **kwargs): + return Followers.objects.filter(followed_by_id=info.context.user.id) + + def resolve_all_followers(self,info,**kwargs): + return Followers.objects.filter(user_id=info.context.user.id) + + def resolve_user(self,info, **kwargs): + user = info.context.user + + if user.is_anonymous: + raise Exception('Not logged in!') + + return user diff --git a/app/api/serializers.py b/app/api/serializers.py deleted file mode 100644 index c19b95a..0000000 --- a/app/api/serializers.py +++ /dev/null @@ -1,34 +0,0 @@ -from rest_framework import serializers -from api.models import Users, Followers - -class UserSerializer(serializers.ModelSerializer): - """ - Serializing all the Users - """ - class Meta: - model = Users - fields = ('id', 'username', 'email') - -class UserDetailSerializer(serializers.ModelSerializer): - """ - Serializing all the Users - """ - class Meta: - model = Users - fields = ('id', 'username', 'email', 'password', 'birthday', 'company', 'location') - -class UserDetailListViewSerializer(serializers.ModelSerializer): - """ - Serializing all the Users - """ - class Meta: - model = Users - fields = ('id', 'username', 'email', 'birthday', 'company', 'location') - -class FollowSerializer(serializers.ModelSerializer): - """ - Serializing Followers - """ - class Meta: - model = Followers - fields = ('id', 'user_id.id', 'followed_by_id.id', 'followed_at') diff --git a/app/api/tests.py b/app/api/tests.py index b7384ca..e69de29 100644 --- a/app/api/tests.py +++ b/app/api/tests.py @@ -1,285 +0,0 @@ -from django.core.urlresolvers import reverse -from rest_framework import status -from rest_framework.test import APITestCase -from api.models import Users -import datetime - -class UserTests(APITestCase): - def test_create_account(self): - """ - Ensure we can create a new user object. - """ - url = reverse('user-create') - data = {'username': 'testing1', 'email': 'testing1@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(Users.objects.count(), 1) - self.assertEqual(Users.objects.get().username, 'testing1') - - data = {'username': 'testing2', 'email': 'testing2@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(Users.objects.count(), 2) - - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - -class LoginTests(APITestCase): - def test_login(self): - u = Users(username='testing3', email='testing3@testing.com', password='password') - u.save() - url = reverse('login') - data = {'email': 'testing3@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - -class LogoutTests(APITestCase): - def test_logout(self) : - u = Users(username='testing4', email='testing4@testing.com', password='password') - u.save() - url = reverse('login') - data = {'email': 'testing4@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - url = reverse('logout') - response = self.client.delete(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - -class ShowUser(APITestCase): - def test_show_user(self): - u = Users(id=5, username='testing5', email='testing5@testing.com', password='password') - u.save() - url = reverse('show-user', args=[5]) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - - - def test_user_not_present(self): - url = reverse('show-user', args=[5]) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - -class UpdateUser(APITestCase): - def test_update_username_without_login(self): - u = Users(id=6, username='testing6', email='testing6@testing.com', password='password') - u.save() - url = reverse('show-user', args=[6]) - data = {'username': 'user6'} - response = self.client.put(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_update_user_with_login(self): - u = Users(id=7, username='testing7', email='testing7@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('login') - data = {'email': 'testing7@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - - if self.assertEqual(response.status_code, status.HTTP_200_OK): - url = reverse('show-user', args=[7]) - data = {'username': 'user7', 'email':'user7@testing.com', 'password':'password7', 'birthday':'1994-04-13', 'company':'Givery', 'location':'Tokyo'} - response = self.client.put(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(Users.objects.get().username, 'user7') - self.assertEqual(Users.objects.get().email, 'user7@testing.com') - self.assertEqual(Users.objects.get().password, 'password7') - self.assertEqual(Users.objects.get().birthday, datetime.date(1994, 4, 13)) - self.assertEqual(Users.objects.get().company, 'Givery') - self.assertEqual(Users.objects.get().location, 'Tokyo') - -class DeleteUser(APITestCase): - def test_delete_username_without_login(self): - u = Users(id=8, username='testing8', email='testing8@testing.com', password='password') - u.save() - url = reverse('show-user', args=[8]) - response = self.client.put(url) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_delete_user_with_login(self): - u = Users(id=9, username='testing9', email='testing9@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('login') - data = {'email': 'testing9@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - - if self.assertEqual(response.status_code, status.HTTP_200_OK): - url = reverse('show-user', args=[9]) - response = self.client.delete(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - -class FollowUser(APITestCase): - def test_follow_user_without_login(self): - u = Users(id=10, username='testing10', email='testing10@testing.com', password='password', birthday='1997-04-17') - u.save() - u = Users(id=11, username='testing11', email='testing11@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('follow', args=[11]) - response = self.client.post(url) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_follow_user_with_login(self): - u = Users(id=12, username='testing12', email='testing12@testing.com', password='password', birthday='1997-04-17') - u.save() - u = Users(id=13, username='testing13', email='testing13@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('login') - data = {'email': 'testing12@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - - if self.assertEqual(response.status_code, status.HTTP_200_OK): - url = reverse('follow', args=[13]) - response = self.client.post(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - -class UnFollowUser(APITestCase): - def test_unfollow_user_without_login(self): - u = Users(id=14, username='testing14', email='testing14@testing.com', password='password', birthday='1997-04-17') - u.save() - u = Users(id=15, username='testing15', email='testing15@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('follow', args=[15]) - response = self.client.delete(url) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_unfollow_user_with_login_without_followed_fail(self): - u = Users(id=16, username='testing16', email='testing16@testing.com', password='password', birthday='1997-04-17') - u.save() - u = Users(id=17, username='testing17', email='testing17@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('login') - data = {'email': 'testing16@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - - if self.assertEqual(response.status_code, status.HTTP_200_OK): - url = reverse('follow', args=[17]) - response = self.client.post(url) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_unfollow_user_with_login_with_followed_success(self): - u = Users(id=18, username='testing18', email='testing18@testing.com', password='password', birthday='1997-04-17') - u.save() - u = Users(id=19, username='testing19', email='testing19@testing.com', password='password', birthday='1997-04-17') - u.save() - - url = reverse('login') - data = {'email': 'testing18@testing.com', 'password': 'password'} - response = self.client.post(url, data, format='json') - - if self.assertEqual(response.status_code, status.HTTP_200_OK): - url = reverse('follow', args=[19]) - response = self.client.post(url) - if self.assertEqual(response.status_code, status.HTTP_200_OK): - url = reverse('follow', args=[19]) - response = self.client.delete(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - -class Pager(APITestCase): - def test_pager_without_param(self): - u = Users(id=20, username='testing20', email='testing20@testing.com', password='password', birthday='1997-04-17') - u.save() - u = Users(id=21, username='testing21', email='testing21@testing.com', password='password', birthday='1991-04-17') - u.save() - u = Users(id=22, username='testing22', email='testing22@testing.com', password='password', birthday='1992-04-17') - u.save() - u = Users(id=23, username='testing23', email='testing23@testing.com', password='password', birthday='1993-04-17') - u.save() - u = Users(id=24, username='testing24', email='testing24@testing.com', password='password', birthday='1994-04-17') - u.save() - u = Users(id=25, username='testing25', email='testing25@testing.com', password='password', birthday='1995-04-17') - u.save() - u = Users(id=26, username='testing26', email='testing26@testing.com', password='password', birthday='1996-04-17') - u.save() - u = Users(id=27, username='testing27', email='testing27@testing.com', password='password', birthday='1991-04-17') - u.save() - u = Users(id=28, username='testing28', email='testing28@testing.com', password='password', birthday='1990-04-17') - u.save() - u = Users(id=29, username='testing29', email='testing29@testing.com', password='password', birthday='1990-04-13') - u.save() - u = Users(id=30, username='testing30', email='testing30@testing.com', password='password', birthday='1991-04-13') - u.save() - - url = reverse('user-list') - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '10') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['username'], 'testing20') - - url = reverse('user-list', kwargs={'offset':5}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '5') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['username'], 'testing25') - - url = reverse('user-list', kwargs={'offset': 0, 'limit':5}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '5') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['username'], 'testing20') - - url = reverse('user-list', kwargs={'offset': 2, 'limit':5}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '3') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['username'], 'testing22') - - url = reverse('user-list', kwargs={'limit':5}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '5') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['username'], 'testing20') - - url = reverse('user-list', kwargs={'orderBy':'birthday'}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '10') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['birthday'], '1990-04-13') - - url = reverse('user-list', kwargs={'orderBy':'birthday', 'order': 'desc'}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '10') - self.assertEqual(response['total_count'], '11') - self.assertEqual(response.data[0]['birthday'], '1997-04-17') - - """ - Add new User 'testing30' to test filtering option - """ - - u = Users(id=31, username='testing30', email='testing31@testing.com', password='password', birthday='1991-04-13') - u.save() - - url = reverse('user-list', kwargs={'filterOn':'username'}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '10') - self.assertEqual(response['total_count'], '12') - - url = reverse('user-list', kwargs={'filterOn':'username', 'filterValue': 'testing30'}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response['count'], '2') - self.assertEqual(response['total_count'], '12') - - - - - - - - - - - - diff --git a/app/api/urls.py b/app/api/urls.py index 84736b9..e69de29 100644 --- a/app/api/urls.py +++ b/app/api/urls.py @@ -1,16 +0,0 @@ -from django.conf.urls import include, url -from api import views -from rest_framework.urlpatterns import format_suffix_patterns - -urlpatterns = [ - # Class Based urls - url(r'^api/users/$', views.UserList.as_view(), name='user-list'), - url(r'^api/users/(?:/(?P\d+))?/(?:/(?P\d+))?/(?:/(?P[a-z]+))?/(?:/(?P[a-z]+))?/(?:/(?P[a-z]+))?/(?:/(?P[a-z]*\d*))?$', views.UserList.as_view(), name='user-list'), - url(r'^api/join$', views.UserCreate.as_view(), name='user-create'), - url(r'^api/login/$', views.UserDetail.as_view(), name='login'), - url(r'^api/logout/$', views.UserDetail.as_view(), name='logout'), - url(r'^api/users/(?P[\d]+)/$', views.UserDetail.as_view(), name='show-user'), - url(r'^api/user/following/(?P[\d]+)/$', views.Follow.as_view(), name='follow'), -] - -urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/app/api/views.py b/app/api/views.py index 5b54f07..e69de29 100644 --- a/app/api/views.py +++ b/app/api/views.py @@ -1,157 +0,0 @@ -from django.http import Http404 -from django.core.exceptions import ObjectDoesNotExist -from rest_framework.views import APIView -from rest_framework import status -from rest_framework.response import Response -from rest_framework import generics -from rest_framework import permissions - -from api.models import Users, Followers -from api.serializers import UserSerializer, UserDetailSerializer, UserDetailListViewSerializer -import random - -class UserList(APIView): - """ - List all users. - """ - def get(self, request, offset=0, limit=10, orderBy='id', order='asc', filterOn=None, filterValue=None,format=None): - if offset is None: - offset = 0 - if limit is None: - limit = 10 - if orderBy == None: - orderBy = 'id' - if order == 'desc': - orderBy = '-' + orderBy - - try: - if filterOn is None or filterValue is None: - users = Users.objects.all().order_by(orderBy)[offset:limit] - count = Users.objects.all()[offset:limit].count() - else: - users = Users.objects.all().filter(**{ filterOn: filterValue }).order_by(orderBy)[offset:limit] - count = Users.objects.all().filter(**{ filterOn: filterValue })[offset:limit].count() - total_count = Users.objects.count() - serializer = UserDetailListViewSerializer(users, many=True) - response = Response() - response['count'] = count - response['total_count'] = total_count - response.data = serializer.data - response.status = status.HTTP_200_OK - return response - except Users.DoesNotExist: - return Response(status=status.HTTP_400_BAD_REQUEST) - -class UserCreate(APIView): - """ - create a new user. - """ - def post(self, request, format=None): - serializer = UserSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - -class UserDetail(APIView): - """ - Retrieve, update or delete a user instance. - """ - def get_object(self, pk): - try: - return Users.objects.get(pk=pk) - except Users.DoesNotExist: - raise Http404 - - def get(self, request, pk, format=None): - user = self.get_object(pk) - serializer = UserSerializer(user) - return Response(serializer.data) - - def put(self, request, pk, format=None): - if 'token' not in request.session: - return Response(status=status.HTTP_400_BAD_REQUEST) - user = self.get_object(pk) - if 'email' not in request.data: - request.data['email'] = user.email - if 'username' not in request.data: - request.data['username'] = user.username - if 'password' not in request.data: - request.data['password'] = user.password - serializer = UserDetailSerializer(user, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk, format=None): - if 'token' not in request.session or 'userId' not in request.session: - return Response(status=status.HTTP_400_BAD_REQUEST) - try: - Users.objects.filter(pk=pk).delete() - return Response(status=status.HTTP_200_OK) - except Users.DoesNotExist: - return Response(status=status.HTTP_400_BAD_REQUEST) - - def post(self, request, format=None): - """ - login. - """ - email = request.data['email'] - password = request.data['password'] - try: - user = Users.objects.get(email=email, password=password) - serializer = UserDetailSerializer(user) - token = '%32x' % random.getrandbits(16*8) - request.session['token'] = token - request.session['userId'] = user.id - return Response(serializer.data, status=status.HTTP_200_OK) - except Users.DoesNotExist: - return Response(request.data, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, format=None): - """ - logout. - """ - try: - del request.session['token'] - del request.session['userId'] - except KeyError: - pass - return Response(status=status.HTTP_200_OK) - -class Follow(APIView): - """ - follow a valid user. - """ - def post(self, request, pk, format=None): - if 'token' not in request.session or 'userId' not in request.session: - return Response(status=status.HTTP_400_BAD_REQUEST) - try: - follower = Users.objects.get(pk=request.session['userId']) - follow = Users.objects.get(pk=pk) - f = Followers(user_id=follow, followed_by_id=follower) - f.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - except Users.DoesNotExist: - return Response(request.data, status=status.HTTP_400_BAD_REQUEST) - - """ - unfollow a followed user. - """ - def delete(self, request, pk, format=None): - if 'token' not in request.session or 'userId' not in request.session: - return Response(status=status.HTTP_400_BAD_REQUEST) - try: - follower = Users.objects.get(pk=request.session['userId']) - unfollow = Users.objects.get(pk=pk) - query = Followers.objects.filter(user_id=unfollow, followed_by_id=follower) - query.delete() - return Response(serializer.data, status=status.HTTP_200_OK) - except follower.DoesNotExist or unfollow.DoesNotExist or query.DoesNotExist: - raise Http404 - - - - - diff --git a/app/app/schema.py b/app/app/schema.py new file mode 100644 index 0000000..a7c815f --- /dev/null +++ b/app/app/schema.py @@ -0,0 +1,16 @@ +import graphene +import graphql_jwt +import api.schema + + +class Query(api.schema.Query, graphene.ObjectType): + pass + + +class Mutation(api.schema.Mutation, graphene.ObjectType,): + token_auth = graphql_jwt.ObtainJSONWebToken.Field() + verify_token = graphql_jwt.Verify.Field() + refresh_token = graphql_jwt.Refresh.Field() + + +schema = graphene.Schema(query=Query, mutation=Mutation) \ No newline at end of file diff --git a/app/app/settings.py b/app/app/settings.py index 1281c64..b499892 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -37,29 +37,18 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'api', - 'rest_framework', - 'django_nose', + 'graphene_django', + 'api' ) -REST_FRAMEWORK = { - 'TEST_REQUEST_DEFAULT_FORMAT': 'json', - 'TEST_REQUEST_RENDERER_CLASSES': ( - 'rest_framework.renderers.MultiPartRenderer', - 'rest_framework.renderers.JSONRenderer', - 'rest_framework.renderers.TemplateHTMLRenderer' - ) -} - -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( + 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.security.SecurityMiddleware', ) ROOT_URLCONF = 'app.urls' @@ -83,6 +72,18 @@ WSGI_APPLICATION = 'app.wsgi.application' +AUTHENTICATION_BACKENDS = [ + 'graphql_jwt.backends.JSONWebTokenBackend', + 'django.contrib.auth.backends.ModelBackend', +] + +GRAPHENE = { + 'SCHEMA': 'app.schema.schema', + 'MIDDLEWARE': [ + 'graphql_jwt.middleware.JSONWebTokenMiddleware', + ], +} + # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases @@ -93,6 +94,23 @@ } } +# Password validation +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ diff --git a/app/app/urls.py b/app/app/urls.py index 2656ca4..0e90ae6 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -15,16 +15,12 @@ """ from django.conf.urls import include, url from django.contrib import admin -from django.contrib.auth.models import User -from rest_framework import routers, serializers, viewsets +from django.urls import path +from django.views.decorators.csrf import csrf_exempt +from graphene_django.views import GraphQLView -from api import views - -# Routers provide an easy way of automatically determining the URL conf. -router = routers.DefaultRouter() urlpatterns = [ - url(r'^', include(router.urls)), - url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), - url(r'^', include('api.urls')), + path('admin/', admin.site.urls), + path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))), ] diff --git a/app/requirements.txt b/app/requirements.txt index 696365b..1df6578 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -10,6 +10,30 @@ nose==1.3.7 passlib==1.6.5 Pygments==2.0.2 requests==2.8.1 -vboxapi==1.0 + Werkzeug==0.11.2 wheel==0.26.0 +aniso8601==7.0.0 +asgiref==3.2.3 +certifi==2019.11.28 +Django==3.0.1 +django-filter==2.2.0 +django-graphql-jwt==0.3.0 +django-nose==1.4.6 +django-templated-mail==1.1.1 +djangorestframework==3.11.0 +djangorestframework-simplejwt==4.4.0 +djoser==2.0.3 +graphene==2.1.8 +graphene-django==2.8.0 +graphql-core==2.2.1 +graphql-relay==2.0.1 +Markdown==3.1.1 +nose==1.3.7 +promise==2.3 +PyJWT==1.7.1 +pytz==2019.3 +Rx==1.6.1 +singledispatch==3.4.0.3 +six==1.13.0 +sqlparse==0.3.0