Fakultas Ilmu Komputer UI

Commit 6457ab9a authored by Jonathan Christopher Jakub's avatar Jonathan Christopher Jakub
Browse files

[RED] Implement reworked account endpoint

parent dd92ebe2
from django_filters import FilterSet, CharFilter
ACCOUNT_FILTERSET_FIELDS = (
"name",
"email",
"phone_number",
"area",
"is_admin",
"is_verified",
"is_active",
)
from apps.accounts.models import Account
ACCOUNT_SEARCH_FIELDS = (
"name",
"email",
"phone_number",
"area",
"is_admin",
"is_verified",
"is_active",
)
class AccountFilter(FilterSet):
username = CharFilter(field_name="user__username")
class Meta:
model = Account
fields = [
"username",
"name",
"email",
"phone_number",
"area",
"is_admin",
"is_verified",
]
ACCOUNT_ORDERING_FIELDS = (
"name",
"email",
"phone_number",
"area",
"is_admin",
"is_verified",
"is_active",
"created_at",
)
# Generated by Django 3.0.1 on 2020-04-18 18:56
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AccountHistory',
fields=[
('revision_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('object_id', models.UUIDField(default=uuid.uuid4, editable=False)),
('action_type', models.CharField(choices=[('Create', 'Create'), ('Edit', 'Edit'), ('Delete', 'Delete')], max_length=64)),
('recorded_at', models.DateTimeField(auto_now_add=True)),
('user_id', models.IntegerField()),
('name', models.CharField(max_length=128)),
('email', models.EmailField(max_length=128)),
('phone_number', models.CharField(max_length=64)),
('area', models.CharField(max_length=128)),
('is_admin', models.BooleanField(default=False)),
('is_verified', models.BooleanField(default=False)),
('is_active', models.BooleanField(default=False)),
],
options={
'verbose_name_plural': 'account histories',
'db_table': 'account_history',
'ordering': ['-recorded_at'],
},
),
migrations.AlterModelOptions(
name='account',
options={'ordering': ['-created_at'], 'verbose_name_plural': 'accounts'},
),
migrations.AddField(
model_name='account',
name='deleted_at',
field=models.DateTimeField(blank=True, null=True),
),
]
......@@ -2,8 +2,32 @@ import uuid
from django.contrib.auth.models import User
from django.db import models
from apps.commons.managers import SoftDeleteManager
from apps.commons.models import HistoryEnabledModel, HistoryModel
class Account(models.Model):
class AccountHistory(HistoryModel):
user_id = models.IntegerField(null=False)
name = models.CharField(max_length=128)
email = models.EmailField(max_length=128)
phone_number = models.CharField(max_length=64)
area = models.CharField(max_length=128)
is_admin = models.BooleanField(default=False)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
objects = models.Manager()
class Meta:
db_table = "account_history"
verbose_name_plural = "account histories"
ordering = ["-recorded_at"]
def __str__(self):
return f"[History] Rev. {self.revision_id} - {self.name}"
class Account(HistoryEnabledModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=128)
......@@ -15,13 +39,16 @@ class Account(models.Model):
is_active = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
objects = models.Manager()
history_class = AccountHistory
relation_fields = ["user"]
objects = SoftDeleteManager()
class Meta:
db_table = "account"
verbose_name_plural = "accounts"
ordering = ["-created_at"]
def __str__(self):
if self.is_admin:
return f"[Admin] {self.user.username}"
return f"[Officer] {self.user.username}"
account_role = "[Admin]" if self.is_admin else "[Officer]"
return f"{account_role} {self.user.username}"
......@@ -5,7 +5,7 @@ from apps.accounts.models import Account
class AccountSerializer(serializers.ModelSerializer):
username = serializers.CharField(source="user.username", required=False)
username = serializers.CharField(source="user.username", read_only=True)
class Meta:
model = Account
......@@ -47,3 +47,8 @@ class AccountRegisterSerializer(serializers.ModelSerializer):
def validate_password(self, value):
password_validation.validate_password(value)
return value
def to_representation(self, instance):
serializer = AccountSerializer(instance)
return serializer.data
......@@ -113,15 +113,15 @@ class AccountViewTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(officer_current_count, officer_prev_count + 1)
# # Have account creation log for the new officer
# new_officer_id = Account.objects.filter(email=_account_id)[0].id
# response = self.client.get("/logs/")
# response_string = response.rendered_content.decode("utf-8")
# Have account creation log for the new officer
new_officer_id = Account.objects.filter(email=_account_id)[0].id
response = self.client.get("/logs/")
response_string = response.rendered_content.decode("utf-8")
# self.assertIn('"object_id":"{}"'.format(new_officer_id), response_string)
# self.assertIn(
# '"action_type":"{}"'.format(ACTIVITY_TYPE_CREATE), response_string
# )
self.assertIn('"object_id":"{}"'.format(new_officer_id), response_string)
self.assertIn(
'"action_type":"{}"'.format(ACTIVITY_TYPE_CREATE), response_string
)
def test_create_new_account_fails_with_poor_password(self):
url = self.BASE_URL
......@@ -164,12 +164,12 @@ class AccountViewTest(APITestCase):
str(response.content, encoding="utf8"), expected_returned_data
)
# # Have account update log
# response = self.client.get("/logs/")
# response_string = response.rendered_content.decode("utf-8")
# Have account update log
response = self.client.get("/logs/")
response_string = response.rendered_content.decode("utf-8")
# self.assertIn('"object_id":"{}"'.format(self.officer.id), response_string)
# self.assertIn('"action_type":"{}"'.format(ACTIVITY_TYPE_EDIT), response_string)
self.assertIn('"object_id":"{}"'.format(self.officer.id), response_string)
self.assertIn('"action_type":"{}"'.format(ACTIVITY_TYPE_EDIT), response_string)
def test_edit_account_fail_without_complete_fields(self):
url = self.BASE_URL + str(self.officer.id) + "/"
......@@ -190,14 +190,14 @@ class AccountViewTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
# # Have account deletion log
# response = self.client.get("/logs/")
# response_string = response.rendered_content.decode("utf-8")
# Have account deletion log
response = self.client.get("/logs/")
response_string = response.rendered_content.decode("utf-8")
# self.assertIn('"object_id":"{}"'.format(self.officer.id), response_string)
# self.assertIn(
# '"action_type":"{}"'.format(ACTIVITY_TYPE_DELETE), response_string
# )
self.assertIn('"object_id":"{}"'.format(self.officer.id), response_string)
self.assertIn(
'"action_type":"{}"'.format(ACTIVITY_TYPE_DELETE), response_string
)
def test_retrieve_current_profile_success(self):
url = self.PROFILE_URL
......
......@@ -3,17 +3,22 @@ from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from apps.accounts.filters import AccountFilter
from apps.accounts.filters import (
ACCOUNT_FILTERSET_FIELDS,
ACCOUNT_ORDERING_FIELDS,
ACCOUNT_SEARCH_FIELDS,
)
from apps.accounts.models import Account
from apps.accounts.serializers import (
AccountSerializer,
AccountRegisterSerializer,
)
from apps.commons.permissions import (
IsSelfOrAdministrator,
IsAuthenticated,
CreateOnly,
)
from apps.constants import (
......@@ -22,85 +27,36 @@ from apps.constants import (
ACTIVITY_TYPE_EDIT,
ACTIVITY_TYPE_DELETE,
)
from apps.logs.models import Log
class AccountViewSet(viewsets.ViewSet):
queryset = Account.objects.all().select_related("user").order_by("-created_at")
filter_backends = (DjangoFilterBackend,)
permission_classes = [
IsSelfOrAdministrator | CreateOnly,
]
def list(self, request):
paginator = PageNumberPagination()
filtered_set = AccountFilter(request.GET, queryset=self.queryset).qs
context = paginator.paginate_queryset(filtered_set, request)
serializer = AccountSerializer(context, many=True)
return paginator.get_paginated_response(serializer.data)
def retrieve(self, request, pk=None):
instance = get_object_or_404(self.queryset, pk=pk)
self.check_object_permissions(request, instance)
serializer = AccountSerializer(instance)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = AccountRegisterSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
class AccountViewSet(viewsets.ModelViewSet):
serializer_class = AccountSerializer
queryset = Account.objects.all()
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
filterset_fields = ACCOUNT_FILTERSET_FIELDS
permission_classes = (IsAuthenticated | CreateOnly, )
pagination_class = PageNumberPagination
search_fields = ACCOUNT_SEARCH_FIELDS
ordering_fields = ACCOUNT_ORDERING_FIELDS
def get_queryset(self):
user = self.request.user
account = Account.objects.filter(user=user)
if account.first().is_admin:
return Account.objects.all()
return account
def get_serializer_class(self):
if self.action in ['create']:
return AccountRegisterSerializer
return AccountSerializer
def perform_create(self, serializer):
username = serializer.validated_data.pop("username").lower()
password = serializer.validated_data.pop("password")
user = User.objects.create_user(username=username, password=password)
account = Account.objects.create(user=user, **serializer.validated_data)
# Add account creation log
Log.objects.create(
model_name=MODEL_NAME_ACCOUNT,
object_id=account.id,
action_type=ACTIVITY_TYPE_CREATE,
author=account,
)
return Response(
AccountSerializer(account).data, status=status.HTTP_201_CREATED,
)
def update(self, request, pk=None):
instance = get_object_or_404(self.queryset, pk=pk)
self.check_object_permissions(request, instance)
serializer = AccountSerializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
# Add account update log
Log.objects.create(
model_name=MODEL_NAME_ACCOUNT,
object_id=pk,
action_type=ACTIVITY_TYPE_EDIT,
author=request.user.account,
)
return Response(serializer.data, status=status.HTTP_200_OK)
def destroy(self, request, pk=None):
instance = get_object_or_404(self.queryset, pk=pk)
self.check_object_permissions(request, instance)
instance.delete()
# Add account deletion log
Log.objects.create(
model_name=MODEL_NAME_ACCOUNT,
object_id=pk,
action_type=ACTIVITY_TYPE_DELETE,
author=request.user.account,
)
serializer = AccountSerializer(instance=instance)
return Response(serializer.data, status=status.HTTP_200_OK)
Account.objects.create(user=user, **serializer.validated_data)
@action(detail=False, methods=["get"], url_path="me")
def profile(self, request):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment