Skip to content

Commit

Permalink
feature(list user):List Users
Browse files Browse the repository at this point in the history
- GET user details
- GET all user profiles
- GET specific user profile
- Add unit tests for feature above
- Add .DS_Store to gitignore

[starts #161967022]
  • Loading branch information
EmmanuelBeja authored and EmmanuelChayu committed Dec 6, 2018
1 parent 9ff6fff commit c30fc49
Show file tree
Hide file tree
Showing 19 changed files with 292 additions and 24 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DATABASE_URL=postgres://emmanuelbeja:12345678@localhost/ahjumanji
DEBUG=on
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ target/
celerybeat-schedule

# dotenv
.env


# virtualenv
venv/
Expand All @@ -93,3 +93,5 @@ ENV/
db.sqlite3

authors/apps/*/migrations/*

*.DS_Store
24 changes: 20 additions & 4 deletions authors/apps/authentication/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@
import logging

import jwt
import os

# from django.conf import settings
#
# from rest_framework import authentication, exceptions
#
# from .models import User
from django.conf import settings
from django.contrib.auth import get_user_model
from rest_framework import exceptions

"""Configure JWT Here"""
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from rest_framework.authentication import TokenAuthentication

# Get an instance of a logger
logger = logging.getLogger(__name__)


class JWTAuthentication(JSONWebTokenAuthentication):
class JWTAuthentication(TokenAuthentication):
"""Inherit the JSON web authentication class from rest_framework_jwt"""

@staticmethod
Expand All @@ -37,6 +39,20 @@ def generate_token(user, is_refresh_token=False):
'nbf': datetime.datetime.utcnow() + datetime.timedelta(minutes=-5),
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=7)
}, secret)
token = str(token)
# decode the byte type token to
token = token.decode('utf-8')
logger.debug("is_refresh_token : %s : %s" % (is_refresh_token, token))
return token

def authenticate_credentials(self, key):
try:
# decode the payload and get the user
payload = jwt.decode(key, settings.SECRET_KEY)
user = get_user_model().objects.get(username=payload['username'])
except (jwt.DecodeError, get_user_model().DoesNotExist):
raise exceptions.AuthenticationFailed('Invalid token')
except jwt.ExpiredSignatureError:
raise exceptions.AuthenticationFailed('Token has expired')
if not user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
return (user, payload)
2 changes: 1 addition & 1 deletion authors/apps/authentication/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 2.1.3 on 2018-11-28 03:50
# Generated by Django 2.1.3 on 2018-12-04 13:34

from django.db import migrations, models

Expand Down
Empty file modified authors/apps/authentication/migrations/__init__.py
100755 → 100644
Empty file.
12 changes: 8 additions & 4 deletions authors/apps/authentication/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ def get_token(self, obj):
:param obj:
:return token:
"""
return JWTAuthentication.generate_token(user=obj, is_refresh_token=False)
return JWTAuthentication.generate_token(
user=obj, is_refresh_token=False)

def get_refresh_token(self, obj):
"""
Generate a refresh token
:return refresh token:
"""
return JWTAuthentication.generate_token(user=obj, is_refresh_token=True)
return JWTAuthentication.generate_token(
user=obj, is_refresh_token=True)


class LoginSerializer(serializers.ModelSerializer):
Expand All @@ -67,14 +69,16 @@ def get_token(self, obj):
:param obj:
:return token:
"""
return JWTAuthentication.generate_token(user=obj, is_refresh_token=True)
return JWTAuthentication.generate_token(
user=obj, is_refresh_token=True)

def get_refresh_token(self, obj):
"""
fetch and return refresh token
:return:
"""
return JWTAuthentication.generate_token(user=obj, is_refresh_token=False)
return JWTAuthentication.generate_token(
user=obj, is_refresh_token=False)

def validate(self, data):
# The `validate` method is where we make sure that the current
Expand Down
46 changes: 46 additions & 0 deletions authors/apps/authentication/tests/test_authors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.test import TestCase
from rest_framework import status
from rest_framework.test import APIClient
from ..models import User


def register_user(
username="Thanos",
email="thanos@avengers.com",
password="avengersassemble"):
"""Creating a test user"""
user = User.objects.create_user(username, email, password)
return user


class TestProfile(TestCase):
"""Test profile."""

def setUp(self):
"""Test Setup."""
self.client = APIClient()
self.login_credentials = {
'user': {
'email': "thanos@avengers.com",
'password': "avengersassemble"
}
}

def login_user(self):
"""Login A user"""
register_user()
response = self.client.post(
'/api/users/login',
data=self.login_credentials,
format='json',
)
return response.data['token']

def test_get_all_users(self):
"""Test get all authors"""
token = self.login_user()
response = self.client.get(
'/api/users/',
HTTP_AUTHORIZATION='Token ' + token
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
14 changes: 10 additions & 4 deletions authors/apps/authentication/tests/test_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,26 @@ def test_create_super_user(self):
assert su.is_superuser is True

def test_user_registration(self, test_client):
response = test_client.post(reverse('register'), data=self.user, format='json')
response = test_client.post(
reverse('register'),
data=self.user,
format='json')
assert response.status_code is 201
assert User.objects.count() > 0

def test_token_in_response(self, test_client):
response = test_client.post(reverse('register'), self.user, format='json')
response = test_client.post(
reverse('register'), self.user, format='json')

assert isinstance(response.data, dict)
assert 'username' in response.data
assert 'token' in response.data

def test_add_existing_user(self, test_client):
response = test_client.post(reverse('register'), self.user, format='json')
response2 = test_client.post(reverse('register'), self.user, format='json')
response = test_client.post(
reverse('register'), self.user, format='json')
response2 = test_client.post(
reverse('register'), self.user, format='json')

assert response2.status_code == 400
assert 'errors' in response2.data
Expand Down
7 changes: 5 additions & 2 deletions authors/apps/authentication/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from django.urls import path

from .views import (
LoginAPIView, RegistrationAPIView, UserRetrieveUpdateAPIView
)
LoginAPIView,
RegistrationAPIView,
UserRetrieveUpdateAPIView,
ListUsersAPIView)

urlpatterns = [
path('user/', UserRetrieveUpdateAPIView.as_view(), name='user-detail'),
path('users/', ListUsersAPIView.as_view(), name='users-List'),
path('users/register', RegistrationAPIView.as_view(), name='register'),
path('users/login', LoginAPIView.as_view(), name='login'),
]
13 changes: 12 additions & 1 deletion authors/apps/authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import generics

from .models import User
from .renderers import UserJSONRenderer
from .serializers import (
LoginSerializer, RegistrationSerializer, UserSerializer
Expand Down Expand Up @@ -38,6 +40,7 @@ class LoginAPIView(APIView):

def post(self, request):
user = request.data.get('user', {})
print(user)

# Notice here that we do not call `serializer.save()` like we did for
# the registration endpoint. This is because we don't actually have
Expand All @@ -49,15 +52,23 @@ def post(self, request):
return Response(serializer.data, status=status.HTTP_200_OK)


class UserRetrieveUpdateAPIView(RetrieveUpdateAPIView):
class ListUsersAPIView(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = User.objects.all()
serializer_class = UserSerializer


class UserRetrieveUpdateAPIView(RetrieveUpdateAPIView):
permission_classes = (AllowAny,)
renderer_classes = (UserJSONRenderer,)
serializer_class = UserSerializer

def retrieve(self, request, *args, **kwargs):
# There is nothing to validate or save here. Instead, we just want the
# serializer to handle turning our `User` object into something that
# can be JSONified and sent to the client.
print('user')
print(request.user)
serializer = self.serializer_class(request.user)

return Response(serializer.data, status=status.HTTP_200_OK)
Expand Down
9 changes: 7 additions & 2 deletions authors/apps/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@

class Base(models.Model):
"""A base class with common fields for all """
created = models.DateTimeField(auto_now_add=True, help_text="This is the time of creation of this record")
modified = models.DateTimeField(auto_now=True, help_text="This field is updated any time this record is updated")
created = models.DateTimeField(
auto_now_add=True,
help_text="This is the time of creation of this record"
)
modified = models.DateTimeField(
auto_now=True,
help_text="This field is updated any time this record is updated")
3 changes: 3 additions & 0 deletions authors/apps/profiles/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from django.contrib import admin

# Register your models here.
from .models import Profile

admin.site.register(Profile)
58 changes: 58 additions & 0 deletions authors/apps/profiles/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,61 @@
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver

# Local imports
from ..authentication.models import User

# Create your models here.


class Profile(models.Model):
"""
Models a user's profile.
Is an extension of app's User model
"""
# Link profile to a User thro one-to-one rlship
user = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)

# Add fields onto `Profile` model
username = models.CharField(max_length=50, blank=True)
email = models.EmailField(blank=True)
full_name = models.CharField(max_length=50, blank=True)
bio = models.CharField(max_length=200, blank=True)
image = models.URLField(blank=True)
country = models.CharField(max_length=3, blank=True)
phone_number = models.CharField(max_length=15, blank=True)

def __str__(self):
"""
Schema for representation of a Profile object in Terminal
"""
return self.email

def update_profile(self, **kwargs):
"""
Update profile with any **kwarg provided, if **kwarg
is in list of fields on Profile model
"""


# Recieve a signal whenever a User is created
# and initialize a Profile tied to the User
@receiver(post_save, sender=User)
def init_profile(sender, instance, created, **kwargs):
"""
Initialize a `Profile` whenever an
instance of a `User` is initialized
"""
if created:
Profile.objects.create(
user=instance, username=instance.username, email=instance.email)


@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
"""
Save the profile initialized above
if User is saved
"""
instance.profile.save()
8 changes: 8 additions & 0 deletions authors/apps/profiles/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rest_framework import serializers
from .models import Profile


class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("__all__")
3 changes: 0 additions & 3 deletions authors/apps/profiles/tests.py

This file was deleted.

Loading

0 comments on commit c30fc49

Please sign in to comment.