Merge branch 'development' into diego

This commit is contained in:
Diego Calvo
2021-03-12 11:32:18 +01:00
16 changed files with 209 additions and 195 deletions

View File

@@ -1,7 +1,7 @@
from rest_framework import routers
from core.views import CustomUserViewSet
from companies.views import CompanyViewSet, MyCompanyViewSet, AdminCompanyViewSet
from companies.views import CompanyViewSet, AdminCompanyViewSet
from products.views import ProductViewSet, MyProductsViewSet, AdminProductsViewSet
from history.views import HistorySyncViewSet
from stats.views import StatsLogViewSet
@@ -13,7 +13,6 @@ router = routers.DefaultRouter()
router.register('users', CustomUserViewSet, basename='users')
router.register('companies', CompanyViewSet, basename='company')
router.register('my_company', MyCompanyViewSet, basename='my-company')
router.register('admin_companies', AdminCompanyViewSet, basename='admin-companies')
router.register('products', ProductViewSet, basename='product')
router.register('my_products', MyProductsViewSet, basename='my-products')

View File

@@ -171,3 +171,6 @@ MAP_WIDGETS = {
),
"GOOGLE_MAP_API_KEY": os.getenv('GOOGLE_MAP_API_KEY')
}
# ACTIVATION_REDIRECT URL
ACTIVATION_REDIRECT = os.getenv('ACTIVATION_REDIRECT')

View File

@@ -39,6 +39,7 @@ urlpatterns = [
path('api/v1/search_products/', product_views.product_search, name='product-search'),
path('api/v1/create_company_user/', core_views.create_company_user, name='create-company-user'),
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/companies/sample/', company_views.random_company_sample , name='company-sample'),
path('api/v1/purchase_email/', product_views.purchase_email, name='purchase-email'),
path('api/v1/stats/me/', stat_views.track_user, name='user-tracker'),

View File

@@ -1,7 +1,7 @@
from django.contrib import admin
from django.contrib.gis.db.models import PointField
from django_admin_listfilter_dropdown.filters import DropdownFilter, RelatedDropdownFilter
from django_admin_listfilter_dropdown.filters import DropdownFilter
from mapwidgets.widgets import GooglePointFieldWidget
from . import models
@@ -10,8 +10,8 @@ from . import models
class CompanyAdmin(admin.ModelAdmin):
list_display = ('short_name', 'city', 'email', 'shop', 'platform', 'sync', 'is_validated', 'is_active', 'link')
list_filter = ('platform', 'sync', 'is_validated', 'is_active', 'city')
search_fields = ('short_name', 'company_name', 'email', 'web_link', ('city', RelatedDropdownFilter))
list_filter = ('platform', 'sync', 'is_validated', 'is_active', ('city', DropdownFilter))
search_fields = ('short_name', 'company_name', 'email', 'web_link', 'city')
formfield_overrides = {
PointField: {"widget": GooglePointFieldWidget}

View File

@@ -146,7 +146,7 @@ class CompanyViewSetTest(APITestCase):
'logo': None,
'city': None,
'address': 'qwer qewr 5',
'geo': None,
'geo': {'longitude': 1.0, 'latitude': 1.0},
'phone': '1234',
'mobile': '4321',
'other_phone': '41423',
@@ -312,12 +312,11 @@ class MyCompanyViewTest(APITestCase):
self.user.set_password(self.password)
self.user.save()
def tearDown(self):
self.model.objects.all().delete()
def test_auth_user_gets_data(self):
# create instance
user_instances = [self.factory(creator=self.user) for i in range(5)]
company = CompanyFactory()
self.user.company = company
self.user.save()
# Authenticate
token = get_tokens_for_user(self.user)
@@ -325,32 +324,10 @@ class MyCompanyViewTest(APITestCase):
# Query endpoint
response = self.client.get(self.endpoint)
payload = response.json()
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEquals(len(user_instances), len(payload))
def test_auth_user_can_paginate_instances(self):
"""authenticated user can paginate instances
"""
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# create instances
instances = [self.factory(creator=self.user) for n in range(12)]
# Request list
url = f"{self.endpoint}?limit=5&offset=10"
response = self.client.get(url)
# Assert access is allowed
self.assertEqual(response.status_code, status.HTTP_200_OK)
# assert only 2 instances in response
payload = response.json()
self.assertEquals(2, len(payload['results']))
self.assertEquals(payload['company']['id'], company.id)
def test_anon_user_cannot_access(self):
# send in request

View File

@@ -155,17 +155,27 @@ class CompanyViewSet(viewsets.ModelViewSet):
return Response(message)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_company(request):
if request.user.company:
serializer = CompanySerializer(request.user.company)
return Response({'company': serializer.data})
else:
return Response(status=status.HTTP_406_NOT_ACCEPTABLE)
'''
class MyCompanyViewSet(viewsets.ModelViewSet):
model = Company
serializer_class = CompanySerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return self.model.objects.filter(creator=self.request.user)
return self.model.objects.filter(company=self.request.user.company)
def perform_create(self, serializer):
serializer.save(creator=self.request.user)
'''
class AdminCompanyViewSet(viewsets.ModelViewSet):
""" Allows user with role 'SITE_ADMIN' to access all company instances

View File

@@ -24,6 +24,8 @@ class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_superuser', False)
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_active', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):

View File

@@ -23,7 +23,7 @@ class CustomUserWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.CustomUser
fields = ('email', 'full_name', 'role', 'password', 'provider')
fields = ('email', 'full_name', 'role', 'password', 'provider', 'notify')
class CreatorSerializer(serializers.ModelSerializer):

View File

@@ -9,6 +9,7 @@ from django.test import TestCase
from django.core import mail
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
from django.conf import settings
from rest_framework.test import APITestCase
from rest_framework import status
@@ -41,7 +42,7 @@ class CustomUserViewSetTest(APITestCase):
self.user = self.factory(email=self.reg_email, password=self.password, is_active=True)
# anon user
def test_anon_user_can_create_active_instance(self):
def test_anon_user_can_create_inactive_instance(self):
"""Not logged-in user can create new instance of User but it's inactive
"""
data = {
@@ -57,7 +58,7 @@ class CustomUserViewSetTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# assert instance is inactive
info = json.loads(response.content)
self.assertTrue(info['is_active'])
self.assertFalse(info['is_active'])
# Assert instance exists on db
self.assertTrue(self.model.objects.get(email=info['email']))
# assert verification email
@@ -161,6 +162,27 @@ class CustomUserViewSetTest(APITestCase):
# Assert access is forbidden
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_auth_user_can_modify_own_instance(self):
"""Regular user can modify own instance
"""
# Create instance
data = {
"email": "new_email@mail.com",
"full_name": "New Full Name",
'provider': 'PROVIDER',
'notify': True,
}
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# Query endpoint
url = f'{self.endpoint}{self.user.pk}/'
response = self.client.put(url, data=data, format='json')
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
# admin user
def test_admin_user_can_create_instance(self):
"""Admin user can create new instance
@@ -332,99 +354,6 @@ class ChangeUserPasswordViewTest(APITestCase):
self.assertEqual(stored_password_hash, base64.b64encode(new_password_hash).decode())
class UpdateUserViewTest(APITestCase):
def setUp(self):
"""Tests setup
"""
self.endpoint = '/api/v1/users/'
self.factory = factories.CustomUserFactory
self.model = models.CustomUser
# create regular user
self.reg_email = f"user@mail.com"
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.user = self.factory(email=self.reg_email, is_active=True)
self.user.set_password(self.password)
self.user.save()
# create admin user
self.admin_email = f"admin_user@mail.com"
self.admin_user = self.factory(email=self.admin_email, is_staff=True, is_active=True)
self.admin_user.set_password(self.password)
self.admin_user.save()
def test_auth_user_can_modify_own_instance(self):
"""Regular user can modify own instance
"""
# Create instance
data = {
"email": "new_email@mail.com",
"full_name": "New Full Name",
'provider': 'PROVIDER',
'notify': True,
}
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# Query endpoint
url = f'{self.endpoint}{self.user.pk}/'
response = self.client.put(url, data=data, format='json')
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_auth_user_cannot_modify_random_instance(self):
"""Regular user cannot modify randnom instance
"""
# Create instance
instance = self.factory()
# Authenticate
token = get_tokens_for_user(self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# Query endpoint
url = f'{self.endpoint}{instance.pk}/'
response = self.client.put(url, data={}, format='json')
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_anon_user_cannot_modify_random_instance(self):
"""anon user cannot modify instance
"""
# Create instance
instance = self.factory()
# Query endpoint
url = f'{self.endpoint}{instance.pk}/'
response = self.client.put(url, data={}, format='json')
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_admin_user_can_modify_random_instance(self):
"""Regular user cannot modify randnom instance
"""
# Create instance
instance = self.factory()
# Authenticate
token = get_tokens_for_user(self.admin_user)
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
data = {
"email": "new_email@mail.com",
"full_name": "New Full Name",
'provider': 'PROVIDER',
'notify': True,
}
# Query endpoint
url = f'{self.endpoint}{instance.pk}/'
response = self.client.put(url, data=data, format='json')
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
class LoadCoopManagerTestCase(APITestCase):
def setUp(self):
@@ -564,6 +493,21 @@ class ActivateUserTest(APITestCase):
response = self.client.get(url)
# assertions
self.assertEquals(response.status_code, 302)
self.assertEquals(response.url, settings.ACTIVATION_REDIRECT)
def test_correct_activation_no_redirect(self):
# set ACTIVATION_REDIRECT to ''
settings.ACTIVATION_REDIRECT = ''
# create values
uid = urlsafe_base64_encode(force_bytes(self.user.pk))
token = account_activation_token.make_token(self.user)
url = f'/activate/{uid}/{token}/'
response = self.client.get(url)
# assertions
self.assertEquals(response.status_code, 200)
self.assertTrue(self.user.email in str(response.content))
@@ -580,3 +524,67 @@ class ActivateUserTest(APITestCase):
# assertions
self.assertEquals(response.status_code, 406)
self.assertTrue('error' in response.json())
class CreateCompanyUserTest(APITestCase):
def setUp(self):
self.endpoint = '/api/v1/create_company_user/'
self.factory = factories.CustomUserFactory
self.model = models.CustomUser
# create user
self.email = "user@mail.com"
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
self.user = self.factory(email=self.email, is_active=False)
self.user.set_password(self.password)
self.user.save()
def test_succesful_creation(self):
data = {
'user': {
'email': 'test@email.com',
'full_name': 'TEST NAME',
'password': 'VENTILADORES1234499.89',
},
'company': {
'cif': 'qwerewq',
'company_name': 'qwerewq',
'short_name': 'qwerewq',
'web_link': 'http://qwerewq.com',
'shop': True,
'shop_link': 'http://qwerewq.com',
'platform': 'PRESTASHOP',
'email': 'test@email.com',
'logo': None,
'city': None,
'address': 'qwer qewr 5',
'geo': {'longitude': 1.0, 'latitude': 1.0},
'phone': '1234',
'mobile': '4321',
'other_phone': '41423',
'description': 'dfgfdgdfg',
'shop_rss_feed': 'http://qwerewq.com',
'sale_terms': 'tewrnmfew f ewfrfew ewewew f',
'shipping_cost': '12.25',
'sync': False
}
}
response = self.client.post(self.endpoint, data=data, format='json')
self.assertEquals(response.status_code, 201)
self.assertEquals(len(mail.outbox), 1)
# user exists and it's inactice
self.assertTrue(self.model.objects.get(email='test@email.com'))
self.assertFalse(self.model.objects.get(email='test@email.com').is_active)
self.assertTrue(Company.objects.get(cif='qwerewq'))
# assert verification email
self.assertTrue(len(mail.outbox) == 1)
def test_creation_error(self):
response = self.client.post(self.endpoint, data={}, format='json')
self.assertEquals(response.status_code, 406)
self.assertEquals(len(mail.outbox), 0)

View File

@@ -11,6 +11,8 @@ from django.utils.http import urlsafe_base64_decode
from django.utils.encoding import force_text
from django.db import IntegrityError
from django.contrib.gis.geos import Point
from django.shortcuts import redirect
from django.conf import settings
from rest_framework import status
from rest_framework import viewsets
@@ -20,6 +22,7 @@ from rest_framework.generics import UpdateAPIView
from rest_framework.decorators import api_view, permission_classes
from companies.models import Company
from companies.serializers import CompanySerializer
from geo.models import City
from . import models
@@ -43,9 +46,9 @@ logging.basicConfig(
class CustomUserViewSet(viewsets.ModelViewSet):
model = models.CustomUser
model = User
model_name = 'custom_user'
queryset = models.CustomUser.objects.all()
queryset = User.objects.all()
permission_classes = [CustomUserPermissions,]
read_serializer_class = core_serializers.CustomUserReadSerializer
write_serializer_class = core_serializers.CustomUserWriteSerializer
@@ -76,7 +79,7 @@ class CustomUserViewSet(viewsets.ModelViewSet):
if serializer.is_valid():
# save model instance data
password = serializer.validated_data.pop('password')
instance = self.model(**serializer.validated_data)
instance = self.model.objects.create_user(**serializer.validated_data)
instance.set_password(password)
instance.save()
# send verification email
@@ -100,63 +103,47 @@ class ChangeUserPasswordView(UpdateAPIView):
serializer_class = core_serializers.ChangePasswordSerializer
class UpdateUserView(UpdateAPIView):
model = models.CustomUser
queryset = model.objects.all()
permission_classes = (YourOwnUserPermissions,)
serializer_class = core_serializers.UpdateUserSerializer
@api_view(['POST',])
@permission_classes([CustomUserPermissions,])
def create_company_user(request):
"""
Create non-validated company and manager user associated
Create non-validated company and associated managing user
"""
user_data = {
'full_name': request.data['user']['full_name'],
'email': request.data['user']['email'],
'password': request.data['user']['password']
}
company_data = {
'cif': request.data['company']['cif'],
'company_name': request.data['company']['company_name'],
'short_name': request.data['company']['short_name'],
'web_link': request.data['company']['web_link'],
'shop': request.data['company']['shop'],
'city': request.data['company']['city'],
'geo': request.data['company']['geo'],
'address': request.data['company']['address']
}
try:
user = models.CustomUser.objects.create(email=user_data['email'], full_name=user_data['full_name'])
except IntegrityError as e:
return Response({"errors": {"details": str(e)}}, status=status.HTTP_409_CONFLICT)
if 'user' not in request.data:
return Response({"error": "Missing parameter: user"}, status=406)
if 'company' not in request.data:
return Response({"error": "Missing parameter: company"}, status=406)
try:
city = company_data.pop('city')
#city = City.objects.get(name=city)
# create company
company_data = request.data['company']
company_serializer = CompanySerializer(
data=company_data,
)
if company_serializer.is_valid():
# save model instance data
new_company = Company.objects.create(**company_serializer.validated_data)
else:
return Response({"error": company_serializer.errors}, status=406)
geo = company_data.pop('geo')
geo = Point(geo['latitude'],geo['longitude'])
# create user
user_data = request.data['user']
user_data['role'] = 'COOP_MANAGER'
user_data['company'] = new_company.id
user_serializer = core_serializers.CustomUserWriteSerializer(
data=user_data,
)
if user_serializer.is_valid():
# save model instance data
password = user_serializer.validated_data.pop('password')
new_user = User.objects.create_user(**user_serializer.validated_data)
new_user.set_password(password)
new_user.save()
# send verification email
utils.send_verification_email(request, new_user)
else:
return Response({"error": user_serializer.errors}, status=406)
company = Company.objects.create(**company_data, city=city, geo=geo)
except Exception as e:
user.delete()
return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
user.set_password(user_data['password'])
user.company = company
user.role = 'COOP_MANAGER'
user.save()
company.creator = user
company.save()
serializer = core_serializers.CustomUserSerializer(user)
return Response(data=serializer.data,status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_201_CREATED)
@api_view(['GET',])
@@ -205,6 +192,8 @@ def activate_user(request, uidb64, token):
# activate user
user.is_active = True
user.save()
if settings.ACTIVATION_REDIRECT:
return redirect(settings.ACTIVATION_REDIRECT)
return Response(f"Tu cuenta de usuario {user.email} ha sido activada")
else:
return Response({"error": f"Tu token de verificacion no coincide con ningún usuario registrado"}, status=status.HTTP_406_NOT_ACCEPTABLE)

View File

@@ -15,3 +15,5 @@ WC_KEY = ''
WC_SECRET = ''
# GOOGLE MAPS
GOOGLE_MAP_API_KEY = ''
# USER ACTIVATION REDIRECTION
ACTIVATION_REDIRECT = ''

View File

@@ -952,9 +952,13 @@ class MyProductsViewTest(APITestCase):
def test_auth_user_gets_data(self):
# create instance
company = CompanyFactory()
self.user.company = company
self.user.save()
user_instances = [
self.factory(creator=self.user),
self.factory(creator=self.user),
self.factory(company=company),
self.factory(company=company),
]
# Authenticate
@@ -976,7 +980,11 @@ class MyProductsViewTest(APITestCase):
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
# create instances
instances = [self.factory(creator=self.user) for n in range(12)]
company = CompanyFactory()
self.user.company = company
self.user.save()
instances = [self.factory(company=company) for n in range(12)]
# Request list
url = f"{self.endpoint}?limit=5&offset=10"
@@ -996,6 +1004,18 @@ class MyProductsViewTest(APITestCase):
# check response
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_auth_user_without_company(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)
payload = response.json()
# Assert forbidden code
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEquals([], payload)
class AdminProductViewSetTest(APITestCase):

View File

@@ -6,6 +6,7 @@ from django.db.models import Q
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity
from django.db.models import Max, Min
from django.conf import settings
from django.utils import timezone
import requests
@@ -203,7 +204,7 @@ def product_loader(csv_reader, user, company=None):
return None
# create historysync instance
history = HistorySync.objects.create(company=company, sync_date=datetime.datetime.now())
history = HistorySync.objects.create(company=company, sync_date=timezone.now())
for row in csv_reader:
# trim strings
for key in row:

View File

@@ -72,10 +72,7 @@ class MyProductsViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
def get_queryset(self):
return self.model.objects.filter(creator=self.request.user)
def perform_create(self, serializer):
serializer.save(creator=self.request.user)
return self.model.objects.filter(company=self.request.user.company).order_by('-created')
class AdminProductsViewSet(viewsets.ModelViewSet):

View File

@@ -116,7 +116,7 @@ class TrackUserViewTest(APITestCase):
'model': 'company',
'id': company.id,
},
'geo': (12.2, -0.545)
'geo': {'latitude': 12.2, 'longitude': -0.545}
}
# Query endpoint

View File

@@ -52,13 +52,18 @@ def track_user(request):
try:
data = json.loads(request.body)
if data.get('geo'):
coordinates = (data['geo'].get('latitude'), data['geo'].get('longitude'))
else:
coordinates = None
# gather instance data
instance_data = {
'action_object': data.get('action_object'),
'user': None if request.user.is_anonymous else request.user,
'anonymous': request.user.is_anonymous,
'ip_address': data.get('ip'),
'geo': Point(data.get('geo')['latitude'], data.get('geo')['longitude']),
'geo': Point(coordinates),
}
if data['action_object'].get('model') == 'product':
@@ -76,4 +81,4 @@ def track_user(request):
return Response(status=status.HTTP_201_CREATED)
except Exception as e:
logging.error(f"Stats could not be created: {str(e)}")
return Response(f"Process could not be registered: {str(type(e))}", status=status.HTTP_406_NOT_ACCEPTABLE)
return Response(f"Process could not be registered [{str(type(e))}]: {str(e)}", status=status.HTTP_406_NOT_ACCEPTABLE)