Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3
ENV PYTHONUNBUFFERED=1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
Empty file added api/__init__.py
Empty file.
Binary file added api/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/admin.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/apps.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/models.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/serializers.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/tests.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/urls.cpython-38.pyc
Binary file not shown.
Binary file added api/__pycache__/views.cpython-38.pyc
Binary file not shown.
5 changes: 5 additions & 0 deletions api/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.contrib import admin
from api.models import Profile

# Register your models here.
admin.site.register(Profile)
5 changes: 5 additions & 0 deletions api/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class ApiConfig(AppConfig):
name = 'api'
28 changes: 28 additions & 0 deletions api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.1.2 on 2020-10-04 00:18

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('avatar', models.CharField(max_length=1024, null=True)),
('token', models.CharField(max_length=1024, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
),
]
Empty file added api/migrations/__init__.py
Empty file.
Binary file added api/migrations/__pycache__/0001_initial.cpython-38.pyc
Binary file not shown.
Binary file added api/migrations/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
10 changes: 10 additions & 0 deletions api/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.db import models
from django.contrib.auth.models import User

# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
avatar = models.CharField(max_length=1024, null=True)
token = models.CharField(max_length=1024, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
36 changes: 36 additions & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from rest_framework import serializers
from django.contrib.auth.models import User
from api.models import Profile
from django.utils.crypto import get_random_string

class UserSerializer(serializers.ModelSerializer):
avatar = serializers.SerializerMethodField('get_avatar')

def get_avatar(self, foo):
try:
return Profile.objects.get(user=foo.id).avatar
except Profile.DoesNotExist:
return None

class Meta:
model = User
fields = ['id', 'email', 'first_name', 'last_name', 'avatar']


class RegisterSerializer(serializers.ModelSerializer):
token = serializers.SerializerMethodField('get_token')

def get_token(self, foo):
try:
return Profile.objects.get(user=foo.id).token
except Profile.DoesNotExist:
return None

def create(self, validated_data):
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, token=get_random_string(length=18))
return user

class Meta:
model = User
fields = ['id', 'email', 'password', 'username', 'token']
69 changes: 69 additions & 0 deletions api/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from django.contrib.auth.models import User
from api.models import Profile

# Create your tests here.
class UserTests(APITestCase):
def test_register_success(self):
url = reverse('register')
data = {
"email": "admin@mail.co",
"password": "admin"
}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(User.objects.count(), 1)
self.assertEqual(User.objects.get().username, 'admin')

def test_register_failed(self):
url = reverse('register')
data = {
"email": "admin@mail.co"
}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_login_success(self):
self.user = User.objects.create(username='admin', email='admin@mail.co', password='admin')
self.profile = Profile.objects.create(user=self.user)
url = reverse('login')
data = {
"email": "admin@mail.co",
"password": "admin"
}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_login_failed(self):
self.user = User.objects.create(username='admin', email='admin@mail.co', password='admin')
self.profile = Profile.objects.create(user=self.user)
url = reverse('login')
data = {
"email": "admin@mail.co"
}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)


def test_user_list(self):
self.user = User.objects.create(username='admin', email='admin@mail.co', password='admin')
self.profile = Profile.objects.create(user=self.user)
url = reverse('user_list')

response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_user_detail(self):
self.user = User.objects.create(username='admin', email='admin@mail.co', password='admin')
self.profile = Profile.objects.create(user=self.user)

url = reverse('user_detail', args=[1])
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

url_2 = reverse('user_detail', args=[2])
response = self.client.get(url_2, format='json')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
13 changes: 13 additions & 0 deletions api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.urls import path
from api import views

urlpatterns = [
path('register', views.register, name='register'),
path('register/', views.register, name='register'),
path('login', views.login, name='login'),
path('login/', views.login, name='login'),
path('users', views.user_list, name='user_list'),
path('users/', views.user_list, name='user_list'),
path('users/<int:pk>', views.user_detail, name='user_detail'),
path('users/<int:pk>/', views.user_detail, name='user_detail'),
]
116 changes: 116 additions & 0 deletions api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from django.contrib.auth.models import User
from api.models import Profile
from api.serializers import UserSerializer, RegisterSerializer
from django.core.paginator import Paginator
import time


# Create your views here.
@csrf_exempt
def user_list(request):
if request.method == 'GET':
page_size = 6
page_number = 1

if 'page' in request.GET:
page_number = request.GET['page']

if 'delay' in request.GET:
delay = request.GET['delay']
time.sleep(int(delay))

users = User.objects.all().order_by('id')
paginator = Paginator(users, page_size)

if int(page_number) > paginator.num_pages:
return HttpResponse(status=404)

paginated_users = paginator.page(page_number).object_list

serializer = UserSerializer(paginated_users, many=True)
Response = {
"page": page_number,
"per_page": page_size,
"total": users.count(),
"total_pages": paginator.num_pages,
"data": serializer.data
}
return JsonResponse(Response, safe=False)


@csrf_exempt
def user_detail(request, pk):
try:
user = User.objects.get(pk=pk)
except User.DoesNotExist:
return HttpResponse(status=404)

if request.method == 'GET':
serializer = UserSerializer(user)
return JsonResponse(serializer.data)
elif request.method == 'PUT' or request.method == 'PATCH':
data = JSONParser().parse(request)
if 'avatar' in data:
avatar = data.pop('avatar')
try:
profile = Profile.objects.get(user=pk)
profile.avatar = avatar
profile.save()
except Profile.DoesNotExist:
Profile.objects.create(user=user, avatar=avatar)
elif 'token' in data:
token = data.pop('token')
try:
profile = Profile.objects.get(user=pk)
profile.token = token
profile.save()
except Profile.DoesNotExist:
Profile.objects.create(user=user, avatar=avatar)

serializer = UserSerializer(user, data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status=400)

elif request.method == 'DELETE':
user.delete()
return HttpResponse(status=204)


@csrf_exempt
def register(request):
if request.method == 'POST':
data = JSONParser().parse(request)
username = data['email'].split('@')[0]
data['username'] = username

serializer = RegisterSerializer(data=data)
if serializer.is_valid():
serializer.save()
Response = {
"id": serializer.data['id'],
"token": serializer.data['token']
}
return JsonResponse(Response)
return HttpResponse(status=404)


@csrf_exempt
def login(request):
if request.method == 'POST':
data = JSONParser().parse(request)

try:
user = User.objects.get(email=data['email'], password=data['password'])
profile = Profile.objects.get(user=user)
Response = {
"token": profile.token
}
return JsonResponse(Response)
except:
return HttpResponse(status=404)
Empty file added codingchallenge/__init__.py
Empty file.
Binary file added codingchallenge/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file added codingchallenge/__pycache__/urls.cpython-38.pyc
Binary file not shown.
Binary file added codingchallenge/__pycache__/wsgi.cpython-38.pyc
Binary file not shown.
16 changes: 16 additions & 0 deletions codingchallenge/asgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
ASGI config for codingchallenge project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codingchallenge.settings')

application = get_asgi_application()
Loading