Merge branch 'development' into diego

This commit is contained in:
Diego Calvo
2021-02-04 12:07:32 +01:00
8 changed files with 241 additions and 10 deletions

View File

@@ -8,6 +8,7 @@ This README aims to document functionality of backend as well as required steps
- [Location Data](#location-data) - [Location Data](#location-data)
- [Endpoints](#endpoints) - [Endpoints](#endpoints)
- [Data Load](#data-load) - [Data Load](#data-load)
- [Development Utils](#development-utils)
## First Steps ## First Steps
@@ -100,8 +101,11 @@ Refresh expired token endpoint: `/api/v1/token/refresh/`
Endpoint url: `/api/v1/users/` Endpoint url: `/api/v1/users/`
To get info on authenticated user: `/api/v1/my_user/`
Authenticated users cannot create new users Authenticated users cannot create new users
User are inactive by default
User are active by default
To create user: To create user:
```json ```json
@@ -118,10 +122,13 @@ To create user:
Endpoint url: `/api/v1/companies/` Endpoint url: `/api/v1/companies/`
To get company linked to authenticated user: `/api/v1/my_company/`
### Products ### Products
Endpoint url: `/api/v1/products/` Endpoint url: `/api/v1/products/`
To get products linked to authenticated user: `/api/v1/my_products/`
### History ### History
@@ -166,3 +173,14 @@ For massive load of product data.
CSV headers: `id,nombre-producto,descripcion,imagen,url,precio,gastos-envio,cond-envio,descuento,stock,tags,categoria,identificadores` CSV headers: `id,nombre-producto,descripcion,imagen,url,precio,gastos-envio,cond-envio,descuento,stock,tags,categoria,identificadores`
Only admin users have access to endoint Only admin users have access to endoint
## Development Utils
### Fake product load
To create a dataset of fake companies and products:
`python manage.py addtestdata`
Creates 10 Companies, with 100 products each.

View File

@@ -22,6 +22,7 @@ from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from core import views as core_views from core import views as core_views
from products import views as product_views from products import views as product_views
from companies import views as company_views
from .routers import router from .routers import router
@@ -34,5 +35,9 @@ urlpatterns = [
path('api/v1/user/update/<int:pk>/', core_views.UpdateUserView.as_view(), name="update-user"), path('api/v1/user/update/<int:pk>/', core_views.UpdateUserView.as_view(), name="update-user"),
path('api/v1/load_coops/', core_views.load_coop_managers, name='coop-loader'), path('api/v1/load_coops/', core_views.load_coop_managers, name='coop-loader'),
path('api/v1/load_products/', product_views.load_coop_products, name='product-loader'), path('api/v1/load_products/', product_views.load_coop_products, name='product-loader'),
path('api/v1/search_products/', product_views.product_search, name='product-search'),
path('api/v1/my_user/', core_views.my_user, name='my-user'),
path('api/v1/my_company/', company_views.my_company , name='my-company'),
path('api/v1/my_products/', product_views.my_products, name='my-products'),
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -239,3 +239,38 @@ class CompanyViewSetTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
# Assert instance doesn't exists anymore on db # Assert instance doesn't exists anymore on db
self.assertFalse(self.model.objects.filter(id=instance.pk).exists()) self.assertFalse(self.model.objects.filter(id=instance.pk).exists())
class MyCompanyViewTest(APITestCase):
"""CompanyViewset tests
"""
def setUp(self):
"""Tests setup
"""
self.endpoint = '/api/v1/my_company/'
self.factory = CompanyFactory
self.model = Company
# create user
self.email = f"user@mail.com"
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.user = CustomUserFactory(email=self.email, is_active=True)
self.user.set_password(self.password)
self.user.save()
def test_auth_user_gets_data(self):
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# Query endpoint
response = self.client.get(self.endpoint)
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_anon_user_cannot_access(self):
# send in request
response = self.client.get(self.endpoint)
# check response
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

View File

@@ -1,8 +1,11 @@
from django.shortcuts import render from django.shortcuts import render
from django.core import serializers
from rest_framework.decorators import api_view, permission_classes
# Create your views here. # Create your views here.
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated
from companies.models import Company from companies.models import Company
from companies.serializers import CompanySerializer from companies.serializers import CompanySerializer
@@ -14,3 +17,11 @@ class CompanyViewSet(viewsets.ModelViewSet):
queryset = Company.objects.all() queryset = Company.objects.all()
serializer_class = CompanySerializer serializer_class = CompanySerializer
permission_classes = [IsAuthenticatedOrReadOnly, IsCreator] permission_classes = [IsAuthenticatedOrReadOnly, IsCreator]
@api_view(['GET',])
@permission_classes([IsAuthenticated,])
def my_company(request):
qs = Company.objects.filter(creator=request.user)
data = serializers.serialize('json', qs)
return Response(data=data)

View File

@@ -460,3 +460,38 @@ class LoadCoopManagerTestCase(APITestCase):
self.assertEqual(company_count, self.company_model.objects.count()) self.assertEqual(company_count, self.company_model.objects.count())
self.assertEqual(user_count, self.user_model.objects.count()) self.assertEqual(user_count, self.user_model.objects.count())
class MyUserViewTest(APITestCase):
"""my_user tests
"""
def setUp(self):
"""Tests setup
"""
self.endpoint = '/api/v1/my_user/'
self.factory = factories.CustomUserFactory
self.model = models.CustomUser
# create user
self.email = f"user@mail.com"
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.user = self.factory(email=self.email, is_active=True)
self.user.set_password(self.password)
self.user.save()
def test_auth_user_gets_data(self):
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# Query endpoint
response = self.client.get(self.endpoint)
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_anon_user_cannot_access(self):
# send in request
response = self.client.get(self.endpoint)
# check response
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

View File

@@ -6,18 +6,19 @@ import io
from django.shortcuts import render from django.shortcuts import render
from django.http import HttpResponse from django.http import HttpResponse
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core import serializers
from rest_framework import status from rest_framework import status
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import IsAdminUser from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.generics import UpdateAPIView from rest_framework.generics import UpdateAPIView
from rest_framework.decorators import api_view, permission_classes from rest_framework.decorators import api_view, permission_classes
from companies.models import Company from companies.models import Company
from . import models from . import models
from . import serializers from . import serializers as core_serializers
from back_latienda.permissions import CustomUserPermissions, YourOwnUserPermissions from back_latienda.permissions import CustomUserPermissions, YourOwnUserPermissions
@@ -37,9 +38,9 @@ logging.basicConfig(
class CustomUserViewSet(viewsets.ModelViewSet): class CustomUserViewSet(viewsets.ModelViewSet):
model = models.CustomUser model = models.CustomUser
# serializer_class = serializers.CustomUserSerializer # serializer_class = core_serializers.CustomUserSerializer
serializer_class = serializers.CustomUserReadSerializer serializer_class = core_serializers.CustomUserReadSerializer
write_serializer_class =serializers.CustomUserWriteSerializer write_serializer_class = core_serializers.CustomUserWriteSerializer
model_name = 'custom_user' model_name = 'custom_user'
queryset = models.CustomUser.objects.all() queryset = models.CustomUser.objects.all()
permission_classes = [CustomUserPermissions,] permission_classes = [CustomUserPermissions,]
@@ -74,7 +75,7 @@ class ChangeUserPasswordView(UpdateAPIView):
model = models.CustomUser model = models.CustomUser
queryset = model.objects.all() queryset = model.objects.all()
permission_classes = (YourOwnUserPermissions,) permission_classes = (YourOwnUserPermissions,)
serializer_class = serializers.ChangePasswordSerializer serializer_class = core_serializers.ChangePasswordSerializer
class UpdateUserView(UpdateAPIView): class UpdateUserView(UpdateAPIView):
@@ -82,9 +83,16 @@ class UpdateUserView(UpdateAPIView):
model = models.CustomUser model = models.CustomUser
queryset = model.objects.all() queryset = model.objects.all()
permission_classes = (YourOwnUserPermissions,) permission_classes = (YourOwnUserPermissions,)
serializer_class = serializers.UpdateUserSerializer serializer_class = core_serializers.UpdateUserSerializer
@api_view(['GET',])
@permission_classes([IsAuthenticated,])
def my_user(request):
qs = User.objects.filter(email=request.user.email)
data = serializers.serialize('json', qs)
return Response(data=data)
@api_view(['POST',]) @api_view(['POST',])
@permission_classes([IsAdminUser,]) @permission_classes([IsAdminUser,])
def load_coop_managers(request): def load_coop_managers(request):

View File

@@ -2,8 +2,10 @@ import random
import string import string
import json import json
import datetime import datetime
from urllib.parse import quote
from django.utils import timezone from django.utils import timezone
from django.test import TestCase
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from rest_framework import status from rest_framework import status
@@ -334,3 +336,72 @@ class LoadCoopProductsTestCase(APITestCase):
# check for object creation # check for object creation
self.assertEqual(0, self.model.objects.count()) self.assertEqual(0, self.model.objects.count())
class ProductSearchTest(TestCase):
def setUp(self):
"""Tests setup
"""
self.endpoint = '/api/v1/search_products/'
self.model = Product
self.factory = ProductFactory
# create admin user
self.admin_email = f"admin_user@mail.com"
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.admin_user = CustomUserFactory(email=self.admin_email, password=self.password, is_staff=True, is_active=True)
# create regular user
self.reg_email = f"user@mail.com"
self.user = CustomUserFactory(email=self.reg_email, is_active=True)
self.user.set_password(self.password)
self.user.save()
def test_anon_user_can_search(self):
self.factory(description="zapatos")
self.factory(tags="rojos")
query_string = quote("zapatos rojos")
url = f"{self.endpoint}?query_string={query_string}"
# send in request
response = self.client.get(url)
import ipdb; ipdb.set_trace()
# check re sponse
self.assertEqual(response.status_code, 200)
# check for object creation
self.assertEquals(2, self.model.objects.count())
class MyProductsViewTest(APITestCase):
"""my_products tests
"""
def setUp(self):
"""Tests setup
"""
self.endpoint = '/api/v1/my_products/'
self.factory = ProductFactory
self.model = Product
# create user
self.email = f"user@mail.com"
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.user = CustomUserFactory(email=self.email, is_active=True)
self.user.set_password(self.password)
self.user.save()
def test_auth_user_gets_data(self):
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# Query endpoint
response = self.client.get(self.endpoint)
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_anon_user_cannot_access(self):
# send in request
response = self.client.get(self.endpoint)
# check response
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

View File

@@ -3,11 +3,12 @@ import csv
from django.shortcuts import render from django.shortcuts import render
from django.conf import settings from django.conf import settings
from django.core import serializers
# Create your views here. # Create your views here.
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAdminUser from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAdminUser, IsAuthenticated
from rest_framework.decorators import api_view, permission_classes from rest_framework.decorators import api_view, permission_classes
import requests import requests
@@ -33,6 +34,14 @@ class ProductViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly, IsCreator] permission_classes = [IsAuthenticatedOrReadOnly, IsCreator]
@api_view(['GET',])
@permission_classes([IsAuthenticated,])
def my_products(request):
qs = Product.objects.filter(creator=request.user)
data = serializers.serialize('json', qs)
return Response(data=data)
@api_view(['POST',]) @api_view(['POST',])
@permission_classes([IsAdminUser,]) @permission_classes([IsAdminUser,])
def load_coop_products(request): def load_coop_products(request):
@@ -100,3 +109,42 @@ def load_coop_products(request):
return Response({"errors": {"details": str(type(e))}}) return Response({"errors": {"details": str(type(e))}})
@api_view(['GET',]) # include allowed methods
def product_search(request):
"""
Takes a string of data, return relevant products
"""
query_string = request.GET.get('query_string', None)
if query_string is None:
return Response({"errors": {"details": "No query string to parse"}})
try:
chunks = query_string.split(' ')
result_set = set()
# import ipdb; ipdb.set_trace()
for chunk in chunks:
# search in name
products = Product.objects.filter(name=chunk)
for item in products:
result_set.add(item)
# search in description
products = Product.objects.filter(description=chunk)
for item in products:
result_set.add(item)
# search in tags
products = Product.objects.filter(tags=chunk)
for item in products:
result_set.add(item)
# search in category
products = Product.objects.filter(category=chunk)
for item in products:
result_set.add(item)
# search in attributes
products = Product.objects.filter(attributes=chunk)
for item in products:
result_set.add(item)
data = serializers.serialize('json', result_set)
return Response(data=data)
except Exception as e:
return Response({"errors": {"details": str(type(e))}})