From 1b7490084b127484fe00cc293dd57d1618649d10 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 09:48:08 +0700 Subject: [PATCH 01/14] Add test to show list of milestones --- core/models/vacancies.py | 3 +++ core/tests/test_vacancies.py | 40 +++++++++++++++++++++++++++++++++++- core/views/vacancies.py | 6 +++++- kape/urls.py | 4 +++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/core/models/vacancies.py b/core/models/vacancies.py index f2b5dc49..9fd1bb0d 100644 --- a/core/models/vacancies.py +++ b/core/models/vacancies.py @@ -32,3 +32,6 @@ class Application(models.Model): class Meta: unique_together = (("student", "vacancy"),) + +class VacancyMilestone(models.Model): + pass diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 1667ac14..15fe1cb4 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -6,7 +6,7 @@ from rest_framework import status from rest_framework.test import APITestCase from core.models.accounts import Company, Student, Supervisor -from core.models.vacancies import Vacancy, Application +from core.models.vacancies import Vacancy, Application, VacancyMilestone class ApplicationTests(APITestCase): @@ -433,3 +433,41 @@ class SupervisorApprovalTests(APITestCase): response = self.client.patch(url, format='json') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(new_vacancy.verified, False) + + +class VacancyMilestoneTests(APITestCase): + def test_vacancy_milestones_list(self): + company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') + new_company = Company.objects.create(user=company_user, description="lalala", status=Company.VERIFIED, logo=None, + address=None) + new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), + description="lalala", close_time=datetime.today()) + milestone1 = VacancyMilestone.objects.create(vacancy=new_vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + + url = '/api/vacancies/' + str(new_vacancy.pk) + '/milestones/' + response = self.client.get(url, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]["name"], milestone1.name) + self.assertEqual(response.data[0]["detail"], milestone1.detail) + + + def test_create_new_milestone_on_a_vacancy_success(self): + pass + + def test_create_new_milestone_on_a_vacancy_unauthorized_user(self): + pass + + def test_modify_milestone_on_a_vacancy_success(self): + pass + + def test_modify_milestone_on_a_vacancy_unauthorized_user(self): + pass + + def test_delete_milestone_on_a_vacancy_success(self): + pass + + def test_delete_milestone_on_a_vacancy_unauthorized_user(self): + pass diff --git a/core/views/vacancies.py b/core/views/vacancies.py index e4b8b989..19b13fc8 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -12,7 +12,7 @@ from core.lib.mixins import MultiSerializerViewSetMixin from core.lib.permissions import IsAdminOrStudent, IsAdminOrCompany, IsAdminOrVacancyOwner, AsAdminOrSupervisor, \ VacancyApprovalPermission from core.models import Student, Company -from core.models.vacancies import Vacancy, Application +from core.models.vacancies import Vacancy, Application, VacancyMilestone from core.serializers.vacancies import VacancySerializer, ApplicationSerializer, ApplicationStatusSerializer, \ PostVacancySerializer, VacancyVerifiedSerializer, SupervisorStudentApplicationSerializer from core.views.accounts import StudentViewSet @@ -275,3 +275,7 @@ class BookmarkedVacancyByStudentViewSet(viewsets.GenericViewSet): student.bookmarked_vacancies.remove(vacancy) return Response( self.serializer_class(student.bookmarked_vacancies, many=True, context={'request': request}).data) + + +class VacancyMilestoneViewSet(viewsets.GenericViewSet): + pass diff --git a/kape/urls.py b/kape/urls.py index 21a1d46b..966abf3d 100755 --- a/kape/urls.py +++ b/kape/urls.py @@ -25,7 +25,7 @@ from core import apps from core.views.accounts import StudentViewSet, CompanyViewSet, SupervisorViewSet, UserViewSet, LoginViewSet, \ CompanyRegisterViewSet from core.views.vacancies import VacancyViewSet, BookmarkedVacancyByStudentViewSet, StudentApplicationViewSet, \ - CompanyApplicationViewSet, CompanyVacanciesViewSet, ApplicationViewSet + CompanyApplicationViewSet, CompanyVacanciesViewSet, ApplicationViewSet, VacancyMilestoneViewSet schema_view = get_swagger_view() router = routers.DefaultRouter() @@ -38,6 +38,8 @@ router.register(r'register', CompanyRegisterViewSet) router.register(r'vacancies', VacancyViewSet) router.register(r'applications', ApplicationViewSet) # router.register(r'students/(?P<student_id>\d+)/profile', StudentProfileViewSet) +router.register(r'vacancies/(?P<vacancy_id>\d+)/milestones', VacancyMilestoneViewSet, + base_name='vacancy-milestones') router.register(r'students/(?P<student_id>\d+)/bookmarked-vacancies', BookmarkedVacancyByStudentViewSet, base_name='bookmarked-vacancy-list') router.register(r'students/(?P<student_id>\d+)/applied-vacancies', StudentApplicationViewSet, -- GitLab From 08120446259f153711f5a8d62e211ce561aee52b Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 10:03:36 +0700 Subject: [PATCH 02/14] Create test for model --- core/tests/test_vacancies.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 15fe1cb4..f164a18a 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -1,6 +1,7 @@ from datetime import datetime import requests_mock +from django.core.exceptions import ValidationError from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase @@ -436,6 +437,26 @@ class SupervisorApprovalTests(APITestCase): class VacancyMilestoneTests(APITestCase): + def test_vacancy_milestone_model(self): + new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), + description="lalala", close_time=datetime.today()) + milestone1 = VacancyMilestone(vacancy=new_vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + milestone1.full_clean() + + milestone2 = VacancyMilestone(vacancy=new_vacancy, name="a"*101, detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + with self.assertRaises(ValidationError, msg="Name with more than 100 character should raise ValidationError"): + milestone2.full_clean() + + milestone3 = VacancyMilestone(vacancy=new_vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(86400), + expected_finish=datetime.fromtimestamp(0)) + with self.assertRaises(ValidationError, msg="Expected finish earlier than tart should raise ValidationError"): + milestone3.full_clean() + def test_vacancy_milestones_list(self): company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') new_company = Company.objects.create(user=company_user, description="lalala", status=Company.VERIFIED, logo=None, -- GitLab From a180f6efbd2fa2b0547d4036aab218faa923e483 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 10:09:53 +0700 Subject: [PATCH 03/14] Fix test mistakes and implement vacancy milestone model --- core/migrations/0014_vacancymilestone.py | 27 ++++++++++++++++++++++++ core/models/vacancies.py | 13 ++++++++++-- core/tests/test_vacancies.py | 3 +++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 core/migrations/0014_vacancymilestone.py diff --git a/core/migrations/0014_vacancymilestone.py b/core/migrations/0014_vacancymilestone.py new file mode 100644 index 00000000..7faa5bd0 --- /dev/null +++ b/core/migrations/0014_vacancymilestone.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.17 on 2019-10-06 03:07 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0013_auto_20170602_1130'), + ] + + operations = [ + migrations.CreateModel( + name='VacancyMilestone', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('detail', models.TextField()), + ('expected_start', models.DateField()), + ('expected_finish', models.DateField()), + ('vacancy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Vacancy')), + ], + ), + ] diff --git a/core/models/vacancies.py b/core/models/vacancies.py index 9fd1bb0d..ebec5763 100644 --- a/core/models/vacancies.py +++ b/core/models/vacancies.py @@ -1,5 +1,5 @@ from django.db import models - +from django.core.exceptions import ValidationError from core.models.accounts import Company, Student @@ -34,4 +34,13 @@ class Application(models.Model): class VacancyMilestone(models.Model): - pass + vacancy = models.ForeignKey(Vacancy, on_delete=models.CASCADE) + name = models.CharField(max_length=100, null=False) + detail = models.TextField() + expected_start = models.DateField() + expected_finish = models.DateField() + + def clean(self): + super(VacancyMilestone, self).clean() + if self.expected_start >= self.expected_finish: + raise ValidationError("Expected start must be earlier than expected finish/") diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index f164a18a..4e4262d5 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -438,6 +438,9 @@ class SupervisorApprovalTests(APITestCase): class VacancyMilestoneTests(APITestCase): def test_vacancy_milestone_model(self): + company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') + new_company = Company.objects.create(user=company_user, description="lalala", status=Company.VERIFIED, logo=None, + address=None) new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today()) milestone1 = VacancyMilestone(vacancy=new_vacancy, name="initiate", detail="install things", -- GitLab From 17f7f153172c603462bb8be59eb856639fd2a3a7 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 10:44:44 +0700 Subject: [PATCH 04/14] Fix test authentication and JSON convention mistake --- core/tests/test_vacancies.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 4e4262d5..50f7efdf 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -461,6 +461,7 @@ class VacancyMilestoneTests(APITestCase): milestone3.full_clean() def test_vacancy_milestones_list(self): + user = User.objects.create_user('dummy.student', 'dummy.student@home.com', 'lalala123') company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') new_company = Company.objects.create(user=company_user, description="lalala", status=Company.VERIFIED, logo=None, address=None) @@ -469,13 +470,14 @@ class VacancyMilestoneTests(APITestCase): milestone1 = VacancyMilestone.objects.create(vacancy=new_vacancy, name="initiate", detail="install things", expected_start=datetime.fromtimestamp(0), expected_finish=datetime.fromtimestamp(86400)) + self.client.force_authenticate(user=user) url = '/api/vacancies/' + str(new_vacancy.pk) + '/milestones/' response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) - self.assertEqual(response.data[0]["name"], milestone1.name) - self.assertEqual(response.data[0]["detail"], milestone1.detail) + self.assertEqual(len(response.data["results"]), 1) + self.assertEqual(response.data["results"][0]["name"], milestone1.name) + self.assertEqual(response.data["results"][0]["detail"], milestone1.detail) def test_create_new_milestone_on_a_vacancy_success(self): -- GitLab From 18434f53c225602ee645666bdcd985d94e4f2f44 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 10:45:48 +0700 Subject: [PATCH 05/14] Update model, and create new serializer and viewset --- core/migrations/0014_vacancymilestone.py | 4 ++-- core/models/vacancies.py | 2 +- core/serializers/vacancies.py | 8 +++++++- core/views/vacancies.py | 21 ++++++++++++++++++--- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/core/migrations/0014_vacancymilestone.py b/core/migrations/0014_vacancymilestone.py index 7faa5bd0..4ac5eba7 100644 --- a/core/migrations/0014_vacancymilestone.py +++ b/core/migrations/0014_vacancymilestone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.17 on 2019-10-06 03:07 +# Generated by Django 1.11.17 on 2019-10-06 03:23 from __future__ import unicode_literals from django.db import migrations, models @@ -21,7 +21,7 @@ class Migration(migrations.Migration): ('detail', models.TextField()), ('expected_start', models.DateField()), ('expected_finish', models.DateField()), - ('vacancy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Vacancy')), + ('vacancy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='milestones', to='core.Vacancy')), ], ), ] diff --git a/core/models/vacancies.py b/core/models/vacancies.py index ebec5763..07591b58 100644 --- a/core/models/vacancies.py +++ b/core/models/vacancies.py @@ -34,7 +34,7 @@ class Application(models.Model): class VacancyMilestone(models.Model): - vacancy = models.ForeignKey(Vacancy, on_delete=models.CASCADE) + vacancy = models.ForeignKey(Vacancy, on_delete=models.CASCADE, related_name="milestones", null=False) name = models.CharField(max_length=100, null=False) detail = models.TextField() expected_start = models.DateField() diff --git a/core/serializers/vacancies.py b/core/serializers/vacancies.py index 313f2064..60300b50 100644 --- a/core/serializers/vacancies.py +++ b/core/serializers/vacancies.py @@ -1,7 +1,7 @@ from rest_framework import serializers from core.models import Company -from core.models.vacancies import Vacancy, Application +from core.models.vacancies import Vacancy, Application, VacancyMilestone from core.serializers.accounts import StudentSerializer, CompanySerializer @@ -88,3 +88,9 @@ class VacancyVerifiedSerializer(serializers.ModelSerializer): class Meta: model = Vacancy fields = ['verified'] + + +class VacancyMilestoneSerializer(serializers.ModelSerializer): + class Meta: + model = VacancyMilestone + fields = ['name', 'detail', 'expected_start', 'expected_finish'] diff --git a/core/views/vacancies.py b/core/views/vacancies.py index 19b13fc8..e37d45d3 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -1,7 +1,7 @@ import requests from django.conf import settings from rest_framework import viewsets, status -from rest_framework.decorators import detail_route +from rest_framework.decorators import detail_route, permission_classes from rest_framework.exceptions import ValidationError from rest_framework.generics import get_object_or_404 from rest_framework.pagination import PageNumberPagination @@ -14,7 +14,8 @@ from core.lib.permissions import IsAdminOrStudent, IsAdminOrCompany, IsAdminOrVa from core.models import Student, Company from core.models.vacancies import Vacancy, Application, VacancyMilestone from core.serializers.vacancies import VacancySerializer, ApplicationSerializer, ApplicationStatusSerializer, \ - PostVacancySerializer, VacancyVerifiedSerializer, SupervisorStudentApplicationSerializer + PostVacancySerializer, VacancyVerifiedSerializer, SupervisorStudentApplicationSerializer, \ + VacancyMilestoneSerializer from core.views.accounts import StudentViewSet @@ -278,4 +279,18 @@ class BookmarkedVacancyByStudentViewSet(viewsets.GenericViewSet): class VacancyMilestoneViewSet(viewsets.GenericViewSet): - pass + serializer_class = VacancyMilestoneSerializer + permission_classes = [IsAuthenticated] + + def list(self, request, vacancy_id): + """ + Get list of a vacancy {vacancy_id}'s milestone plans + --- + """ + vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id) + milestones = vacancy.milestones.all() + page = self.paginate_queryset(milestones) + if page is not None: + return self.get_paginated_response( + VacancyMilestoneSerializer(page, many=True, context={'request': request}).data) + return Response(VacancyMilestoneSerializer(milestones, many=True, context={'request': request}).data) -- GitLab From 7feee1e8d5f34d322edcfb3be0d313953b22cc6f Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 11:09:05 +0700 Subject: [PATCH 06/14] Refactor permission for vacancy milestone --- core/lib/permissions.py | 15 +++++++++++++++ core/models/__init__.py | 5 +---- core/views/vacancies.py | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/lib/permissions.py b/core/lib/permissions.py index f3ad630e..79122c0f 100644 --- a/core/lib/permissions.py +++ b/core/lib/permissions.py @@ -6,6 +6,7 @@ from core.models import Student from core.models import Supervisor from core.models import Application from core.models import Vacancy +from core.models import VacancyMilestone def is_admin_or_student(user): @@ -159,3 +160,17 @@ class VacancyApprovalPermission(permissions.BasePermission): def has_object_permission(self, request, view, obj): return isinstance(obj, Vacancy) + +class IsAdminOrVacancyOwnerOrAuthenticatedReadOnly(permissions.IsAuthenticated): + def has_object_permission(self, request, view, obj): + user = request.user + if request.user and request.method in permissions.SAFE_METHODS: + return True + if request.user.is_superuser or request.user.is_staff: + return True + if isinstance(obj, VacancyMilestone): + return user.company == obj.vacancy.company + else: + raise PermissionDenied( + "Checking owner permission on non-milestone object" + ) diff --git a/core/models/__init__.py b/core/models/__init__.py index 8ca55685..c5561b1e 100755 --- a/core/models/__init__.py +++ b/core/models/__init__.py @@ -4,7 +4,4 @@ from core.models.accounts import Student from core.models.accounts import Supervisor from core.models.vacancies import Vacancy from core.models.vacancies import Application - - - - +from core.models.vacancies import VacancyMilestone diff --git a/core/views/vacancies.py b/core/views/vacancies.py index e37d45d3..c5596da7 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -10,7 +10,7 @@ from rest_framework.response import Response from core.lib.mixins import MultiSerializerViewSetMixin from core.lib.permissions import IsAdminOrStudent, IsAdminOrCompany, IsAdminOrVacancyOwner, AsAdminOrSupervisor, \ - VacancyApprovalPermission + VacancyApprovalPermission, IsAdminOrVacancyOwnerOrAuthenticatedReadOnly from core.models import Student, Company from core.models.vacancies import Vacancy, Application, VacancyMilestone from core.serializers.vacancies import VacancySerializer, ApplicationSerializer, ApplicationStatusSerializer, \ @@ -280,7 +280,7 @@ class BookmarkedVacancyByStudentViewSet(viewsets.GenericViewSet): class VacancyMilestoneViewSet(viewsets.GenericViewSet): serializer_class = VacancyMilestoneSerializer - permission_classes = [IsAuthenticated] + permission_classes = [IsAdminOrVacancyOwnerOrAuthenticatedReadOnly] def list(self, request, vacancy_id): """ -- GitLab From 8a00e46c57aacf9fcc499b8cf698f21f65445d4e Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 12:31:55 +0700 Subject: [PATCH 07/14] Add tests on create vacancy milestones --- core/tests/test_vacancies.py | 61 ++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 50f7efdf..6c3d736b 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -437,42 +437,40 @@ class SupervisorApprovalTests(APITestCase): class VacancyMilestoneTests(APITestCase): - def test_vacancy_milestone_model(self): - company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') - new_company = Company.objects.create(user=company_user, description="lalala", status=Company.VERIFIED, logo=None, + def setUp(self): + super(VacancyMilestoneTests, self).setUp() + self.user = User.objects.create_user('dummy.student', 'dummy.student@home.com', 'lalala123') + self.company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') + self.company = Company.objects.create(user=self.company_user, description="lalala", status=Company.VERIFIED, logo=None, address=None) - new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), + self.vacancy = Vacancy.objects.create(company=self.company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today()) - milestone1 = VacancyMilestone(vacancy=new_vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + + def test_vacancy_milestone_model(self): + milestone1 = VacancyMilestone(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) milestone1.full_clean() - milestone2 = VacancyMilestone(vacancy=new_vacancy, name="a"*101, detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone2 = VacancyMilestone(vacancy=self.vacancy, name="a"*101, detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) with self.assertRaises(ValidationError, msg="Name with more than 100 character should raise ValidationError"): milestone2.full_clean() - milestone3 = VacancyMilestone(vacancy=new_vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(86400), - expected_finish=datetime.fromtimestamp(0)) + milestone3 = VacancyMilestone(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(86400), + expected_finish=datetime.fromtimestamp(0)) with self.assertRaises(ValidationError, msg="Expected finish earlier than tart should raise ValidationError"): milestone3.full_clean() def test_vacancy_milestones_list(self): - user = User.objects.create_user('dummy.student', 'dummy.student@home.com', 'lalala123') - company_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') - new_company = Company.objects.create(user=company_user, description="lalala", status=Company.VERIFIED, logo=None, - address=None) - new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), - description="lalala", close_time=datetime.today()) - milestone1 = VacancyMilestone.objects.create(vacancy=new_vacancy, name="initiate", detail="install things", + milestone1 = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", expected_start=datetime.fromtimestamp(0), expected_finish=datetime.fromtimestamp(86400)) - self.client.force_authenticate(user=user) + self.client.force_authenticate(user=self.user) - url = '/api/vacancies/' + str(new_vacancy.pk) + '/milestones/' + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' response = self.client.get(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data["results"]), 1) @@ -481,10 +479,25 @@ class VacancyMilestoneTests(APITestCase): def test_create_new_milestone_on_a_vacancy_success(self): - pass + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + data = {"name": "initiate", "detail": "install things", "expected_start": "2019-01-20", + "expected_finish": "2019-01-21"} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.vacancy.milestones.count(), 1) + new_milestone = self.vacancy.milestones.first() + self.assertEqual(new_milestone.name, data["name"]) + self.assertEqual(new_milestone.detail, data["detail"]) def test_create_new_milestone_on_a_vacancy_unauthorized_user(self): - pass + self.client.force_authenticate(user=self.user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + data = {"name": "initiate", "detail": "install things", "expected_start": "2019-01-20", + "expected_finish": "2019-01-21"} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(self.vacancy.milestones.count(), 0) def test_modify_milestone_on_a_vacancy_success(self): pass -- GitLab From 86f2cf33d5243f0e97c9eecb37f4fe3f34bdf9ed Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 12:42:46 +0700 Subject: [PATCH 08/14] Add new test on invalid create input --- core/tests/test_vacancies.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 6c3d736b..fbccc316 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -477,7 +477,6 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(response.data["results"][0]["name"], milestone1.name) self.assertEqual(response.data["results"][0]["detail"], milestone1.detail) - def test_create_new_milestone_on_a_vacancy_success(self): self.client.force_authenticate(user=self.company_user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' @@ -490,6 +489,15 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(new_milestone.name, data["name"]) self.assertEqual(new_milestone.detail, data["detail"]) + def test_create_new_milestone_on_a_vacancy_invalid_data(self): + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + data = {"name": "initiate", "detail": "install things", "expected_start": "2019-01-20", + "expected_finish": "2019-01-01"} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(self.vacancy.milestones.count(), 0) + def test_create_new_milestone_on_a_vacancy_unauthorized_user(self): self.client.force_authenticate(user=self.user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' @@ -502,6 +510,9 @@ class VacancyMilestoneTests(APITestCase): def test_modify_milestone_on_a_vacancy_success(self): pass + def test_modify_milestone_on_a_vacancy_invalid_data(self): + pass + def test_modify_milestone_on_a_vacancy_unauthorized_user(self): pass -- GitLab From 0b7e0e07d998faf3afca8b5ee6086afa92c26bb6 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 13:02:19 +0700 Subject: [PATCH 09/14] Implement create new milestones Also updates permission and serializer --- core/lib/permissions.py | 17 +++++++++++------ core/models/vacancies.py | 2 +- core/serializers/vacancies.py | 5 +++++ core/views/vacancies.py | 21 +++++++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/core/lib/permissions.py b/core/lib/permissions.py index 79122c0f..a4f7b829 100644 --- a/core/lib/permissions.py +++ b/core/lib/permissions.py @@ -162,15 +162,20 @@ class VacancyApprovalPermission(permissions.BasePermission): return isinstance(obj, Vacancy) class IsAdminOrVacancyOwnerOrAuthenticatedReadOnly(permissions.IsAuthenticated): + def has_permission(self, request, view): + is_authenticated = super(IsAdminOrVacancyOwnerOrAuthenticatedReadOnly, self).has_permission(request, view) + if is_authenticated and request.method in permissions.SAFE_METHODS: + return True + return is_admin_or_company(request.user) + def has_object_permission(self, request, view, obj): user = request.user - if request.user and request.method in permissions.SAFE_METHODS: + if user and request.method in permissions.SAFE_METHODS: return True - if request.user.is_superuser or request.user.is_staff: + if user.is_superuser or user.is_staff: return True if isinstance(obj, VacancyMilestone): return user.company == obj.vacancy.company - else: - raise PermissionDenied( - "Checking owner permission on non-milestone object" - ) + raise PermissionDenied( + "Checking owner permission on non-milestone object" + ) diff --git a/core/models/vacancies.py b/core/models/vacancies.py index 07591b58..0aaa8ef3 100644 --- a/core/models/vacancies.py +++ b/core/models/vacancies.py @@ -43,4 +43,4 @@ class VacancyMilestone(models.Model): def clean(self): super(VacancyMilestone, self).clean() if self.expected_start >= self.expected_finish: - raise ValidationError("Expected start must be earlier than expected finish/") + raise ValidationError("Expected start must be earlier than expected finish.") diff --git a/core/serializers/vacancies.py b/core/serializers/vacancies.py index 60300b50..1093e28e 100644 --- a/core/serializers/vacancies.py +++ b/core/serializers/vacancies.py @@ -91,6 +91,11 @@ class VacancyVerifiedSerializer(serializers.ModelSerializer): class VacancyMilestoneSerializer(serializers.ModelSerializer): + def validate(self, data): + if data['expected_start'] > data['expected_finish']: + raise serializers.ValidationError("Expected start must be earlier than expected finish.") + return data + class Meta: model = VacancyMilestone fields = ['name', 'detail', 'expected_start', 'expected_finish'] diff --git a/core/views/vacancies.py b/core/views/vacancies.py index c5596da7..c44090fd 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -294,3 +294,24 @@ class VacancyMilestoneViewSet(viewsets.GenericViewSet): return self.get_paginated_response( VacancyMilestoneSerializer(page, many=True, context={'request': request}).data) return Response(VacancyMilestoneSerializer(milestones, many=True, context={'request': request}).data) + + def create(self, request, vacancy_id): + """ + Create a new milestone for vacancy {vacancy_id} + --- + parameters: + - name: body + description: JSON object containing 'name' string, 'detail' string, 'expected_start' date string, and + 'expected_finish' date string + required: true + type: string + paramType: body + """ + vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id) + milestone_serializer = VacancyMilestoneSerializer(data=request.data) + if milestone_serializer.is_valid(): + milestone = milestone_serializer.save(vacancy=vacancy) + return Response(VacancyMilestoneSerializer(milestone, context={'request': request}).data, + status=status.HTTP_200_OK) + print(milestone_serializer.errors) + return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST) -- GitLab From 31482b05c87fd0404e2694d17f68c13a06819ef7 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 13:49:33 +0700 Subject: [PATCH 10/14] Add tests on milestone modify --- core/tests/test_vacancies.py | 42 +++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index fbccc316..060d0b36 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -508,13 +508,49 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(self.vacancy.milestones.count(), 0) def test_modify_milestone_on_a_vacancy_success(self): - pass + milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' + data = {"name": "initiate env", "detail": "install all things", "expected_start": "2019-01-20", + "expected_finish": "2019-01-21"} + response = self.client.put(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.vacancy.milestones.count(), 1) + new_milestone = self.vacancy.milestones.first() + self.assertEqual(new_milestone.name, data["name"]) + self.assertEqual(new_milestone.detail, data["detail"]) def test_modify_milestone_on_a_vacancy_invalid_data(self): - pass + milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' + data = {"name": "a" * 101, "detail": "install all things", "expected_start": "2019-01-21", + "expected_finish": "2019-01-19"} + response = self.client.put(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(self.vacancy.milestones.count(), 1) + new_milestone = self.vacancy.milestones.first() + self.assertEqual(new_milestone.name, milestone.name) + self.assertEqual(new_milestone.detail, milestone.detail) def test_modify_milestone_on_a_vacancy_unauthorized_user(self): - pass + milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + self.client.force_authenticate(user=self.user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' + data = {"name": "initiate env", "detail": "install all things", "expected_start": "2019-01-20", + "expected_finish": "2019-01-21"} + response = self.client.put(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(self.vacancy.milestones.count(), 1) + new_milestone = self.vacancy.milestones.first() + self.assertEqual(new_milestone.name, milestone.name) + self.assertEqual(new_milestone.detail, milestone.detail) def test_delete_milestone_on_a_vacancy_success(self): pass -- GitLab From 4fd44541f035cfc5218c9bb29491b963dfa2b3b5 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 14:06:18 +0700 Subject: [PATCH 11/14] Implement update milestone viewset method --- core/views/vacancies.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/core/views/vacancies.py b/core/views/vacancies.py index c44090fd..0c3a688c 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -313,5 +313,25 @@ class VacancyMilestoneViewSet(viewsets.GenericViewSet): milestone = milestone_serializer.save(vacancy=vacancy) return Response(VacancyMilestoneSerializer(milestone, context={'request': request}).data, status=status.HTTP_200_OK) - print(milestone_serializer.errors) + return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def update(self, request, vacancy_id, pk): + """ + Create existing milestone {pk} for vacancy {vacancy_id} + --- + parameters: + - name: body + description: JSON object containing 'name' string, 'detail' string, 'expected_start' date string, and + 'expected_finish' date string + required: true + type: string + paramType: body + """ + vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id) + old_milestone = get_object_or_404(vacancy.milestones.all(), pk=pk) + milestone_serializer = VacancyMilestoneSerializer(old_milestone, data=request.data) + if milestone_serializer.is_valid(): + milestone = milestone_serializer.save(vacancy=vacancy) + return Response(VacancyMilestoneSerializer(milestone, context={'request': request}).data, + status=status.HTTP_200_OK) return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST) -- GitLab From dca24ed5d69c28919225bbd53d82a37ebd6acaa1 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 14:32:57 +0700 Subject: [PATCH 12/14] Add tests on delete milestones --- core/tests/test_vacancies.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 060d0b36..105b9fe7 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -553,7 +553,21 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(new_milestone.detail, milestone.detail) def test_delete_milestone_on_a_vacancy_success(self): - pass + milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' + response = self.client.delete(url, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.vacancy.milestones.count(), 0) def test_delete_milestone_on_a_vacancy_unauthorized_user(self): - pass + milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + self.client.force_authenticate(user=self.user) + url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' + response = self.client.delete(url, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(self.vacancy.milestones.count(), 1) -- GitLab From 5bff1f1708738ed6dbd9e9fa3ef4e36f4b57deba Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 14:54:17 +0700 Subject: [PATCH 13/14] Refactor test and add tests for "object not found" --- core/tests/test_vacancies.py | 56 ++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index 105b9fe7..e5552b17 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -446,6 +446,11 @@ class VacancyMilestoneTests(APITestCase): self.vacancy = Vacancy.objects.create(company=self.company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today()) + def create_milestone_object(self): + return VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", + expected_start=datetime.fromtimestamp(0), + expected_finish=datetime.fromtimestamp(86400)) + def test_vacancy_milestone_model(self): milestone1 = VacancyMilestone(vacancy=self.vacancy, name="initiate", detail="install things", expected_start=datetime.fromtimestamp(0), @@ -465,9 +470,7 @@ class VacancyMilestoneTests(APITestCase): milestone3.full_clean() def test_vacancy_milestones_list(self): - milestone1 = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone1 = self.create_milestone_object() self.client.force_authenticate(user=self.user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' @@ -508,9 +511,7 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(self.vacancy.milestones.count(), 0) def test_modify_milestone_on_a_vacancy_success(self): - milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone = self.create_milestone_object() self.client.force_authenticate(user=self.company_user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' data = {"name": "initiate env", "detail": "install all things", "expected_start": "2019-01-20", @@ -522,10 +523,24 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(new_milestone.name, data["name"]) self.assertEqual(new_milestone.detail, data["detail"]) + def test_modify_milestone_on_a_vacancy_not_found(self): + milestone = self.create_milestone_object() + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/1000/milestones/' + str(milestone.pk) + '/' + data = {"name": "initiate env", "detail": "install all things", "expected_start": "2019-01-20", + "expected_finish": "2019-01-21"} + response = self.client.put(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + url2 = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/1000/' + response2 = self.client.put(url2, data, format='json') + self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(self.vacancy.milestones.count(), 1) + new_milestone = self.vacancy.milestones.first() + self.assertEqual(new_milestone.name, milestone.name) + self.assertEqual(new_milestone.detail, milestone.detail) + def test_modify_milestone_on_a_vacancy_invalid_data(self): - milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone = self.create_milestone_object() self.client.force_authenticate(user=self.company_user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' data = {"name": "a" * 101, "detail": "install all things", "expected_start": "2019-01-21", @@ -538,9 +553,7 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(new_milestone.detail, milestone.detail) def test_modify_milestone_on_a_vacancy_unauthorized_user(self): - milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone = self.create_milestone_object() self.client.force_authenticate(user=self.user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' data = {"name": "initiate env", "detail": "install all things", "expected_start": "2019-01-20", @@ -553,19 +566,26 @@ class VacancyMilestoneTests(APITestCase): self.assertEqual(new_milestone.detail, milestone.detail) def test_delete_milestone_on_a_vacancy_success(self): - milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone = self.create_milestone_object() self.client.force_authenticate(user=self.company_user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' response = self.client.delete(url, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(self.vacancy.milestones.count(), 0) + def test_delete_milestone_on_a_vacancy_not_found(self): + milestone = self.create_milestone_object() + self.client.force_authenticate(user=self.company_user) + url = '/api/vacancies/1000/milestones/' + str(milestone.pk) + '/' + response = self.client.delete(url, format='json') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + url2 = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/1000/' + response2 = self.client.delete(url2, format='json') + self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(self.vacancy.milestones.count(), 1) + def test_delete_milestone_on_a_vacancy_unauthorized_user(self): - milestone = VacancyMilestone.objects.create(vacancy=self.vacancy, name="initiate", detail="install things", - expected_start=datetime.fromtimestamp(0), - expected_finish=datetime.fromtimestamp(86400)) + milestone = self.create_milestone_object() self.client.force_authenticate(user=self.user) url = '/api/vacancies/' + str(self.vacancy.pk) + '/milestones/' + str(milestone.pk) + '/' response = self.client.delete(url, format='json') -- GitLab From 3c64981a9b7ce817a46f1a233d1ba69d59aa5d81 Mon Sep 17 00:00:00 2001 From: Ichlasul Affan <ichlaffterlalu@gmail.com> Date: Sun, 6 Oct 2019 14:57:13 +0700 Subject: [PATCH 14/14] Implement delete milestones --- core/views/vacancies.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/core/views/vacancies.py b/core/views/vacancies.py index 0c3a688c..2588f117 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -292,8 +292,8 @@ class VacancyMilestoneViewSet(viewsets.GenericViewSet): page = self.paginate_queryset(milestones) if page is not None: return self.get_paginated_response( - VacancyMilestoneSerializer(page, many=True, context={'request': request}).data) - return Response(VacancyMilestoneSerializer(milestones, many=True, context={'request': request}).data) + self.serializer_class(page, many=True, context={'request': request}).data) + return Response(self.serializer_class(milestones, many=True, context={'request': request}).data) def create(self, request, vacancy_id): """ @@ -308,10 +308,10 @@ class VacancyMilestoneViewSet(viewsets.GenericViewSet): paramType: body """ vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id) - milestone_serializer = VacancyMilestoneSerializer(data=request.data) + milestone_serializer = self.serializer_class(data=request.data) if milestone_serializer.is_valid(): milestone = milestone_serializer.save(vacancy=vacancy) - return Response(VacancyMilestoneSerializer(milestone, context={'request': request}).data, + return Response(self.serializer_class(milestone, context={'request': request}).data, status=status.HTTP_200_OK) return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -329,9 +329,20 @@ class VacancyMilestoneViewSet(viewsets.GenericViewSet): """ vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id) old_milestone = get_object_or_404(vacancy.milestones.all(), pk=pk) - milestone_serializer = VacancyMilestoneSerializer(old_milestone, data=request.data) + milestone_serializer = self.serializer_class(old_milestone, data=request.data) if milestone_serializer.is_valid(): milestone = milestone_serializer.save(vacancy=vacancy) - return Response(VacancyMilestoneSerializer(milestone, context={'request': request}).data, + return Response(self.serializer_class(milestone, context={'request': request}).data, status=status.HTTP_200_OK) return Response(milestone_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def destroy(self, request, vacancy_id, pk): + """ + Remove existing milestone {pk} from vacancy {vacancy_id} + --- + """ + vacancy = get_object_or_404(Vacancy.objects.all(), pk=vacancy_id) + milestone = get_object_or_404(vacancy.milestones.all(), pk=pk) + milestone.delete() + return Response( + self.serializer_class(vacancy.milestones.all(), many=True, context={'request': request}).data) -- GitLab