mirror of
https://github.com/suitenumerique/docs.git
synced 2026-09-13 21:27:53 +02:00
✨(backend) add limit on distinct reactions per comment
Implement a configurable limit (default: 15) on the number of distinct emoji reactions per comment. - Backend validation ensures the limit cannot be exceeded via API Signed-off-by: Mohamed El Amine BOUKERFA <boukerfa.ma@gmail.com>
This commit is contained in:
@@ -8,6 +8,7 @@ and this project adheres to
|
||||
|
||||
### Added
|
||||
|
||||
- ✨(backend) add limit on distinct reactions per comment #1978
|
||||
- ✨(frontend) leave a document #2410
|
||||
- ✨(frontend) add top parent on sub docs search #1952
|
||||
- ✨(frontend) unauthenticated users can search #2407
|
||||
|
||||
@@ -3088,6 +3088,7 @@ class ConfigView(drf.views.APIView):
|
||||
"POSTHOG_HOST",
|
||||
"LANGUAGES",
|
||||
"LANGUAGE_CODE",
|
||||
"REACTIONS_MAX_PER_COMMENT",
|
||||
"SENTRY_DSN",
|
||||
"TRASHBIN_CUTOFF_DAYS",
|
||||
]
|
||||
@@ -3233,7 +3234,9 @@ class CommentViewSet(
|
||||
permission_classes = [permissions.CommentPermission]
|
||||
pagination_class = Pagination
|
||||
serializer_class = serializers.CommentSerializer
|
||||
queryset = models.Comment.objects.select_related("user").all()
|
||||
queryset = models.Comment.objects.select_related("user").prefetch_related(
|
||||
"reactions__users"
|
||||
).all()
|
||||
|
||||
def get_queryset(self):
|
||||
"""Override to filter on related resource."""
|
||||
@@ -3279,9 +3282,29 @@ class CommentViewSet(
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
if request.method == "POST":
|
||||
emoji = serializer.validated_data["emoji"]
|
||||
|
||||
if (
|
||||
not models.Reaction.objects.filter(
|
||||
comment=comment, emoji=emoji
|
||||
).exists()
|
||||
and comment.reactions.count() >= settings.REACTIONS_MAX_PER_COMMENT
|
||||
):
|
||||
return drf.response.Response(
|
||||
{
|
||||
"emoji": [
|
||||
_(
|
||||
"A comment can have a maximum of %(max)d distinct reactions."
|
||||
)
|
||||
% {"max": settings.REACTIONS_MAX_PER_COMMENT}
|
||||
]
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
reaction, created = models.Reaction.objects.get_or_create(
|
||||
comment=comment,
|
||||
emoji=serializer.validated_data["emoji"],
|
||||
emoji=emoji,
|
||||
)
|
||||
if not created and reaction.users.filter(id=request.user.id).exists():
|
||||
return drf.response.Response(
|
||||
|
||||
@@ -227,6 +227,11 @@ class ReactionFactory(factory.django.DjangoModelFactory):
|
||||
comment = factory.SubFactory(CommentFactory)
|
||||
emoji = factory.Faker("emoji")
|
||||
|
||||
@classmethod
|
||||
def generate_emojis(cls, n=10):
|
||||
"""Generate a list of n unique emojis."""
|
||||
return [fake.unique.emoji() for _ in range(n)]
|
||||
|
||||
@factory.post_generation
|
||||
def users(self, create, extracted, **kwargs):
|
||||
"""Add users to reaction from a given list of users or create one if not provided."""
|
||||
|
||||
@@ -957,3 +957,56 @@ def test_delete_reaction_owned_by_the_current_user():
|
||||
|
||||
reaction.refresh_from_db()
|
||||
assert reaction.users.exists()
|
||||
|
||||
|
||||
def test_create_reaction_exceeds_maximum(settings):
|
||||
"""
|
||||
Users should not be able to add more than REACTIONS_MAX_PER_COMMENT
|
||||
(here we set it to 10) distinct emoji reactions to a comment.
|
||||
They should, however, be able to add themselves to an existing reaction.
|
||||
"""
|
||||
user1 = factories.UserFactory()
|
||||
user2 = factories.UserFactory()
|
||||
document = factories.DocumentFactory(
|
||||
link_reach="restricted",
|
||||
users=[(user1, models.RoleChoices.ADMIN), (user2, models.RoleChoices.ADMIN)],
|
||||
)
|
||||
thread = factories.ThreadFactory(document=document)
|
||||
comment = factories.CommentFactory(thread=thread)
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user1)
|
||||
|
||||
# Add max distinct reactions
|
||||
max_reactions = settings.REACTIONS_MAX_PER_COMMENT
|
||||
emojis = factories.ReactionFactory.generate_emojis(max_reactions + 1)
|
||||
for emoji in emojis[:max_reactions]:
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/threads/{thread.id!s}/"
|
||||
f"comments/{comment.id!s}/reactions/",
|
||||
{"emoji": emoji},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
# Attempt to add another distinct reaction
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/threads/{thread.id!s}/"
|
||||
f"comments/{comment.id!s}/reactions/",
|
||||
{"emoji": emojis[max_reactions]},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
expected_message = (
|
||||
f"A comment can have a maximum of {max_reactions} distinct reactions."
|
||||
)
|
||||
assert response.json() == {"emoji": [expected_message]}
|
||||
|
||||
# Attempt to add user2 to one of the existing reactions (should succeed)
|
||||
client.force_login(user2)
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/threads/{thread.id!s}/"
|
||||
f"comments/{comment.id!s}/reactions/",
|
||||
{"emoji": emojis[0]},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
reaction = models.Reaction.objects.get(comment=comment, emoji=emojis[0])
|
||||
assert reaction.users.count() == 2
|
||||
|
||||
@@ -80,6 +80,7 @@ def test_api_config(is_authenticated):
|
||||
"MEDIA_BASE_URL": "http://testserver/",
|
||||
"POSTHOG_KEY": "132456",
|
||||
"POSTHOG_HOST": "https://eu.i.posthog-test.com",
|
||||
"REACTIONS_MAX_PER_COMMENT": 15,
|
||||
"RELEASE_VERSION": "1.0.0",
|
||||
"SENTRY_DSN": "https://sentry.test/123",
|
||||
"TRASHBIN_CUTOFF_DAYS": 30,
|
||||
|
||||
@@ -197,6 +197,12 @@ class Base(Configuration):
|
||||
environ_prefix=None,
|
||||
)
|
||||
|
||||
REACTIONS_MAX_PER_COMMENT = values.IntegerValue(
|
||||
15,
|
||||
environ_name="REACTIONS_MAX_PER_COMMENT",
|
||||
environ_prefix=None,
|
||||
)
|
||||
|
||||
DOCUMENT_UNSAFE_MIME_TYPES = [
|
||||
# Executable Files
|
||||
"application/x-msdownload",
|
||||
|
||||
@@ -42,6 +42,7 @@ export const CONFIG = {
|
||||
LANGUAGE_CODE: 'en-us',
|
||||
POSTHOG_HOST: 'https://eu.i.posthog.com',
|
||||
POSTHOG_KEY: null,
|
||||
REACTIONS_MAX_PER_COMMENT: 15,
|
||||
RELEASE_VERSION: packageJsonVersion,
|
||||
SENTRY_DSN: null,
|
||||
TRASHBIN_CUTOFF_DAYS: 30,
|
||||
|
||||
Reference in New Issue
Block a user