mirror of
https://github.com/ae-utbm/sith.git
synced 2025-01-31 03:11:10 +00:00
API to moderate and delete news
This commit is contained in:
parent
18967cf3d6
commit
74385bde04
27
com/api.py
27
com/api.py
@ -5,6 +5,8 @@ from django.http import Http404
|
|||||||
from ninja_extra import ControllerBase, api_controller, route
|
from ninja_extra import ControllerBase, api_controller, route
|
||||||
|
|
||||||
from com.calendar import IcsCalendar
|
from com.calendar import IcsCalendar
|
||||||
|
from com.models import News
|
||||||
|
from core.auth.api_permissions import HasPerm
|
||||||
from core.views.files import send_raw_file
|
from core.views.files import send_raw_file
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +19,7 @@ class CalendarController(ControllerBase):
|
|||||||
"""Return the ICS file of the AE Google Calendar
|
"""Return the ICS file of the AE Google Calendar
|
||||||
|
|
||||||
Because of Google's cors rules, we can't just do a request to google ics
|
Because of Google's cors rules, we can't just do a request to google ics
|
||||||
from the frontend. Google is blocking CORS request in it's responses headers.
|
from the frontend. Google is blocking CORS request in its responses headers.
|
||||||
The only way to do it from the frontend is to use Google Calendar API with an API key
|
The only way to do it from the frontend is to use Google Calendar API with an API key
|
||||||
This is not especially desirable as your API key is going to be provided to the frontend.
|
This is not especially desirable as your API key is going to be provided to the frontend.
|
||||||
|
|
||||||
@ -30,3 +32,26 @@ class CalendarController(ControllerBase):
|
|||||||
@route.get("/internal.ics", url_name="calendar_internal")
|
@route.get("/internal.ics", url_name="calendar_internal")
|
||||||
def calendar_internal(self):
|
def calendar_internal(self):
|
||||||
return send_raw_file(IcsCalendar.get_internal())
|
return send_raw_file(IcsCalendar.get_internal())
|
||||||
|
|
||||||
|
|
||||||
|
@api_controller("/news")
|
||||||
|
class NewsController(ControllerBase):
|
||||||
|
@route.patch(
|
||||||
|
"/{news_id}/moderate",
|
||||||
|
permissions=[HasPerm("com.moderate_news")],
|
||||||
|
url_name="moderate_news",
|
||||||
|
)
|
||||||
|
def moderate_news(self, news_id: int):
|
||||||
|
news = self.get_object_or_exception(News, id=news_id)
|
||||||
|
if not news.is_moderated:
|
||||||
|
news.is_moderated = True
|
||||||
|
news.save()
|
||||||
|
|
||||||
|
@route.delete(
|
||||||
|
"/{news_id}",
|
||||||
|
permissions=[HasPerm("com.delete_news")],
|
||||||
|
url_name="delete_news",
|
||||||
|
)
|
||||||
|
def delete_news(self, news_id: int):
|
||||||
|
news = self.get_object_or_exception(News, id=news_id)
|
||||||
|
news.delete()
|
||||||
|
@ -6,12 +6,16 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from model_bakery import baker
|
||||||
|
|
||||||
from com.calendar import IcsCalendar
|
from com.calendar import IcsCalendar
|
||||||
|
from com.models import News
|
||||||
|
from core.models import User
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -120,3 +124,58 @@ class TestInternalCalendar:
|
|||||||
out_file = accel_redirect_to_file(response)
|
out_file = accel_redirect_to_file(response)
|
||||||
assert out_file is not None
|
assert out_file is not None
|
||||||
assert out_file.exists()
|
assert out_file.exists()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestModerateNews:
|
||||||
|
@pytest.mark.parametrize("news_is_moderated", [True, False])
|
||||||
|
def test_moderation_ok(self, client: Client, news_is_moderated: bool): # noqa FBT
|
||||||
|
user = baker.make(
|
||||||
|
User, user_permissions=[Permission.objects.get(codename="moderate_news")]
|
||||||
|
)
|
||||||
|
# The API call should work even if the news is initially moderated.
|
||||||
|
# In the latter case, the result should be a noop, rather than an error.
|
||||||
|
news = baker.make(News, is_moderated=news_is_moderated)
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.patch(
|
||||||
|
reverse("api:moderate_news", kwargs={"news_id": news.id})
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
news.refresh_from_db()
|
||||||
|
assert news.is_moderated
|
||||||
|
|
||||||
|
def test_moderation_forbidden(self, client: Client):
|
||||||
|
user = baker.make(User)
|
||||||
|
news = baker.make(News, is_moderated=False)
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.patch(
|
||||||
|
reverse("api:moderate_news", kwargs={"news_id": news.id})
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
news.refresh_from_db()
|
||||||
|
assert not news.is_moderated
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestDeleteNews:
|
||||||
|
def test_delete_news_ok(self, client: Client):
|
||||||
|
user = baker.make(
|
||||||
|
User, user_permissions=[Permission.objects.get(codename="delete_news")]
|
||||||
|
)
|
||||||
|
news = baker.make(News)
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
reverse("api:delete_news", kwargs={"news_id": news.id})
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert not News.objects.filter(id=news.id).exists()
|
||||||
|
|
||||||
|
def test_delete_news_forbidden(self, client: Client):
|
||||||
|
user = baker.make(User)
|
||||||
|
news = baker.make(News)
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
reverse("api:delete_news", kwargs={"news_id": news.id})
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
assert News.objects.filter(id=news.id).exists()
|
||||||
|
Loading…
Reference in New Issue
Block a user