diff --git a/django_email_learning/api/urls.py b/django_email_learning/api/urls.py index d561b58..96a788a 100644 --- a/django_email_learning/api/urls.py +++ b/django_email_learning/api/urls.py @@ -6,6 +6,7 @@ OrganizationsView, SingleCourseView, CourseContentView, + SingleCourseContentView, UpdateSessionView, ) @@ -32,6 +33,11 @@ CourseContentView.as_view(), name="course_content_view", ), + path( + "organizations//courses//contents//", + SingleCourseContentView.as_view(), + name="single_course_content_view", + ), path("organizations/", OrganizationsView.as_view(), name="organizations_view"), path("session", UpdateSessionView.as_view(), name="update_session_view"), path("", page_not_found, name="root"), diff --git a/django_email_learning/api/views.py b/django_email_learning/api/views.py index ff1f57a..b52bbb0 100644 --- a/django_email_learning/api/views.py +++ b/django_email_learning/api/views.py @@ -9,6 +9,7 @@ from django_email_learning.api import serializers from django_email_learning.models import ( Course, + CourseContent, ImapConnection, OrganizationUser, Organization, @@ -98,6 +99,23 @@ def get(self, request, *args, **kwargs) -> JsonResponse: # type: ignore[no-unty return JsonResponse({"error": "Course not found"}, status=404) +@method_decorator(accessible_for(roles={"admin", "editor"}), name="delete") +class SingleCourseContentView(View): + def delete(self, request, *args, **kwargs): # type: ignore[no-untyped-def] + try: + course_content = CourseContent.objects.get(id=kwargs["course_content_id"]) + course_content.delete() + return JsonResponse( + {"message": "Course content deleted successfully"}, status=200 + ) + except CourseContent.DoesNotExist: + return JsonResponse({"error": "Course content not found"}, status=404) + except ValidationError as e: + return JsonResponse({"error": e.errors()}, status=400) + except (IntegrityError, ValueError) as e: + return JsonResponse({"error": str(e)}, status=409) + + @method_decorator(accessible_for(roles={"admin", "editor"}), name="post") @method_decorator(accessible_for(roles={"admin", "editor"}), name="delete") @method_decorator(accessible_for(roles={"admin", "editor", "viewer"}), name="get") diff --git a/tests/api/test_views/test_course_content_view.py b/tests/api/test_views/test_course_content_view.py index 5325133..55545d4 100644 --- a/tests/api/test_views/test_course_content_view.py +++ b/tests/api/test_views/test_course_content_view.py @@ -344,3 +344,82 @@ def test_list_course_content_with_existing_contents(superadmin_client, create_co } assert "lesson" not in data["course_contents"][0] assert "quiz" not in data["course_contents"][1] + + +def test_delete_course_content(superadmin_client, create_course): + url = get_url() + # Create a lesson content + lesson_payload = { + "content": { + "title": LESSON_TITLE, + "content": LESSON_CONTENT, + "type": "lesson", + }, + "priority": 1, + "waiting_period": {"period": 2, "type": "days"}, + } + response = superadmin_client.post( + url, json.dumps(lesson_payload), content_type="application/json" + ) + assert response.status_code == 201 + data = response.json() + course_content_id = data["id"] + + # Delete the created course content + delete_url = reverse( + "django_email_learning:api:single_course_content_view", + kwargs={ + "organization_id": 1, + "course_id": 1, + "course_content_id": course_content_id, + }, + ) + contents_response = superadmin_client.get(url) + assert contents_response.status_code == 200 + assert len(contents_response.json()["course_contents"]) == 1 + + delete_response = superadmin_client.delete(delete_url) + assert delete_response.status_code == 200 + + # Verify that the course content is deleted + contents_response_after_delete = superadmin_client.get(url) + assert contents_response_after_delete.status_code == 200 + assert len(contents_response_after_delete.json()["course_contents"]) == 0 + get_response = superadmin_client.delete(delete_url) + assert get_response.status_code == 404 + + +def test_viewer_cannot_delete_course_content(viewer_client, create_course): + url = get_url() + # Create a lesson content + lesson_payload = { + "content": { + "title": LESSON_TITLE, + "content": LESSON_CONTENT, + "type": "lesson", + }, + "priority": 1, + "waiting_period": {"period": 2, "type": "days"}, + } + response = viewer_client.post( + url, json.dumps(lesson_payload), content_type="application/json" + ) + assert response.status_code == 403 + + +def test_anonymous_user_cannot_delete_course_content(anonymous_client, create_course): + url = get_url() + # Create a lesson content + lesson_payload = { + "content": { + "title": LESSON_TITLE, + "content": LESSON_CONTENT, + "type": "lesson", + }, + "priority": 1, + "waiting_period": {"period": 2, "type": "days"}, + } + response = anonymous_client.post( + url, json.dumps(lesson_payload), content_type="application/json" + ) + assert response.status_code == 401