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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
91 changes: 87 additions & 4 deletions app/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,30 @@
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

import os
from pathlib import Path
from datetime import timedelta
from decouple import config
import dj_database_url

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# See https://docs.djangoproject.com/en/4.0/hosto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-h-h69cr05lmc*w4vtkf+5qltg8#&#xf8fe(v9j9oxs-*-^#vjd'
SECRET_KEY = config("SECRET_KEY")

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []
ALLOWED_HOSTS = [
'139.59.230.211',
'localhost',
'127.0.0.1',
]


# Application definition
Expand All @@ -37,9 +45,26 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'core'
'core',
'users',
'products',
'orders',
'rest_framework',
'rest_framework_simplejwt',
'corsheaders',
]

REST_FRAMEWORK = {

'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 10

}

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
Expand All @@ -48,6 +73,7 @@
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
]

ROOT_URLCONF = 'app.urls'
Expand All @@ -68,19 +94,33 @@
},
]


WSGI_APPLICATION = 'app.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': config('DB_NAME'),
'USER': config('DB_USER'),
'PASSWORD': config('DB_PASSWORD'),
'HOST': config('DB_HOST'),
'PORT': config('DB_PORT', '5432'),
}
}

""" DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}

DATABASES['default'] = dj_database_url.parse(config('DATABASE_URL')) """


# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
Expand All @@ -100,6 +140,45 @@
},
]

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(hours=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=90),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"UPDATE_LAST_LOGIN": False,

"ALGORITHM": "HS256",
"VERIFYING_KEY": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,

"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",

"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",

"JTI_CLAIM": "jti",

"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),

"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
Expand All @@ -118,6 +197,10 @@

STATIC_URL = 'static/'

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

STATICFILES_DIRS = [BASE_DIR / 'static']

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

Expand Down
6 changes: 5 additions & 1 deletion app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('users.urls')),
path('api/', include('products.urls')),
path('api/', include('orders.urls')),
path('', include('core.urls')),
]
6 changes: 3 additions & 3 deletions app/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.0.1 on 2022-02-19 10:18
# Generated by Django 5.0.7 on 2024-07-18 16:08

from django.db import migrations, models

Expand All @@ -23,8 +23,8 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=255)),
('is_active', models.BooleanField(default=True)),
('is_staff', models.BooleanField(default=False)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'abstract': False,
Expand Down
148 changes: 148 additions & 0 deletions app/core/tests/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import json
from django.urls import reverse
from django.test import TestCase, TransactionTestCase
from rest_framework import status
from rest_framework.test import APITestCase
from django.contrib.auth import get_user_model
from products.models import Category, Product
from orders.models import Order

User = get_user_model()

class CategoryTests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(name='testuser', email='example@gmail.com', password='testpassword')
self.token = self.get_jwt_token()
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
self.category_url = reverse('category-list-create')

def get_jwt_token(self):
url = reverse('token_obtain_pair')
response = self.client.post(url, {'email': 'example@gmail.com', 'password': 'testpassword'}, format='json')
return response.data['access']

def test_create_category(self):
data = {"category_name": "Gadgets"}
response = self.client.post(self.category_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Category.objects.count(), 1)
self.assertEqual(Category.objects.get().category_name, 'Gadgets')

def test_list_categories(self):
Category.objects.create(category_name='Gadgets')
response = self.client.get(self.category_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 1)
self.assertEqual(response.data['results'][0]['category_name'], 'Gadgets')

class ProductTests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(name='testuser', email='example@gmail.com', password='testpassword')
self.token = self.get_jwt_token()
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
self.category = Category.objects.create(category_name='Gadgets')
self.product_url = reverse('product-list-create')

def get_jwt_token(self):
url = reverse('token_obtain_pair')
response = self.client.post(url, {'email': 'example@gmail.com', 'password': 'testpassword'}, format='json')
return response.data['access']

def test_create_product(self):
data = {
"name": "SmartWatch",
"description": "Powerful smartwatch for techies",
"price": 1500.00,
"stock": 10,
"category": {
"category_name": "Gadgets"
}
}
response = self.client.post(self.product_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Product.objects.count(), 1)
self.assertEqual(Product.objects.get().name, 'SmartWatch')

def test_list_products(self):
Product.objects.create(
name="SmartWatch",
description="Powerful smartwatch for techies",
price=1500.00,
stock=10,
category=self.category
)
response = self.client.get(self.product_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 1)
self.assertEqual(response.data['results'][0]['name'], 'SmartWatch')

def test_retrieve_product(self):
product = Product.objects.create(
name="SmartWatch",
description="Powerful smartwatch for techies",
price=1500.00,
stock=10,
category=self.category
)
url = reverse('product-detail', args=[product.id])
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['name'], 'SmartWatch')

def test_delete_product(self):
product = Product.objects.create(
name="SmartWatch",
description="Powerful smartwatch for techies",
price=1500.00,
stock=10,
category=self.category
)
url = reverse('product-detail', args=[product.id])
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Product.objects.count(), 0)

class OrderTests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(name='testuser', email='example@gmail.com', password='testpassword')
self.token = self.get_jwt_token()
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
self.category = Category.objects.create(category_name='Gadgets')
self.product = Product.objects.create(
name="SmartWatch",
description="A smart watch",
price=199.99,
stock=100,
category=self.category
)

self.order_create_url = reverse('order-create')
self.order_history_url = reverse('order-history')

def get_jwt_token(self):
url = reverse('token_obtain_pair')
response = self.client.post(url, {'email': 'example@gmail.com', 'password': 'testpassword'}, format='json')
return response.data['access']

def test_create_order(self):
data = {
"products": [self.product.id],
"quantity": 2
}
response = self.client.post(self.order_create_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Order.objects.count(), 1)
order = Order.objects.get()
self.assertEqual(order.user, self.user)
self.assertEqual(order.products.first(), self.product)
self.assertEqual(order.quantity, 2)

def test_order_history(self):
Order.objects.create(user=self.user, quantity=2)
response = self.client.get(self.order_history_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 1)

order = response.data['results'][0]
self.assertEqual(order['quantity'], 2)
self.assertEqual(order['user'], self.user.id)
6 changes: 6 additions & 0 deletions app/core/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path
from core.views import homepage

urlpatterns = [
path('', homepage, name='homepage'),
]
4 changes: 4 additions & 0 deletions app/core/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.http import JsonResponse

def homepage(request):
return JsonResponse({'message': 'Test APIs are Live!'})
Empty file added app/orders/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions app/orders/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions app/orders/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class OrdersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'orders'
28 changes: 28 additions & 0 deletions app/orders/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 5.0.7 on 2024-07-20 02:43

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


class Migration(migrations.Migration):

initial = True

dependencies = [
('products', '0004_alter_product_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField()),
('date', models.DateTimeField(auto_now_add=True)),
('products', models.ManyToManyField(to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
Empty file.
Loading