diff --git a/stream/exceptions.py b/stream/exceptions.py index 6b1f70a..00e9b70 100644 --- a/stream/exceptions.py +++ b/stream/exceptions.py @@ -15,7 +15,6 @@ def __unicode__(self): class ApiKeyException(StreamApiException): - """ Raised when there is an issue with your Access Key """ @@ -25,7 +24,6 @@ class ApiKeyException(StreamApiException): class SignatureException(StreamApiException): - """ Raised when there is an issue with the signature you provided """ @@ -35,7 +33,6 @@ class SignatureException(StreamApiException): class InputException(StreamApiException): - """ Raised when you send the wrong data to the API """ @@ -45,7 +42,6 @@ class InputException(StreamApiException): class CustomFieldException(StreamApiException): - """ Raised when there are missing or misconfigured custom fields """ @@ -55,7 +51,6 @@ class CustomFieldException(StreamApiException): class FeedConfigException(StreamApiException): - """ Raised when there are missing or misconfigured custom fields """ @@ -65,7 +60,6 @@ class FeedConfigException(StreamApiException): class SiteSuspendedException(StreamApiException): - """ Raised when the site requesting the data is suspended """ @@ -75,7 +69,6 @@ class SiteSuspendedException(StreamApiException): class InvalidPaginationException(StreamApiException): - """ Raised when there is an issue with your Access Key """ @@ -108,7 +101,6 @@ class RankingException(FeedConfigException): class RateLimitReached(StreamApiException): - """ Raised when too many requests are performed """ diff --git a/stream/reactions/base.py b/stream/reactions/base.py index 31e2842..7038a90 100644 --- a/stream/reactions/base.py +++ b/stream/reactions/base.py @@ -11,6 +11,7 @@ def add( data=None, target_feeds=None, target_feeds_extra_data=None, + moderation_template=None, ): pass @@ -39,6 +40,7 @@ def add_child( data=None, target_feeds=None, target_feeds_extra_data=None, + moderation_template=None, ): pass diff --git a/stream/reactions/reaction.py b/stream/reactions/reaction.py index f65403c..ac2ec2e 100644 --- a/stream/reactions/reaction.py +++ b/stream/reactions/reaction.py @@ -10,6 +10,7 @@ def add( data=None, target_feeds=None, target_feeds_extra_data=None, + moderation_template=None, ): payload = dict( kind=kind, @@ -19,6 +20,8 @@ def add( target_feeds_extra_data=target_feeds_extra_data, user_id=user_id, ) + if moderation_template is not None: + payload["moderation_template"] = moderation_template return self.client.post( self.API_ENDPOINT, service_name=self.SERVICE_NAME, @@ -65,6 +68,7 @@ def add_child( data=None, target_feeds=None, target_feeds_extra_data=None, + moderation_template=None, ): payload = dict( kind=kind, @@ -74,6 +78,8 @@ def add_child( target_feeds_extra_data=target_feeds_extra_data, user_id=user_id, ) + if moderation_template is not None: + payload["moderation_template"] = moderation_template return self.client.post( self.API_ENDPOINT, service_name=self.SERVICE_NAME, @@ -100,6 +106,7 @@ async def add( data=None, target_feeds=None, target_feeds_extra_data=None, + moderation_template=None, ): payload = dict( kind=kind, @@ -109,6 +116,8 @@ async def add( target_feeds_extra_data=target_feeds_extra_data, user_id=user_id, ) + if moderation_template is not None: + payload["moderation_template"] = moderation_template return await self.client.post( self.API_ENDPOINT, service_name=self.SERVICE_NAME, @@ -155,6 +164,7 @@ async def add_child( data=None, target_feeds=None, target_feeds_extra_data=None, + moderation_template=None, ): payload = dict( kind=kind, @@ -164,6 +174,8 @@ async def add_child( target_feeds_extra_data=target_feeds_extra_data, user_id=user_id, ) + if moderation_template is not None: + payload["moderation_template"] = moderation_template return await self.client.post( self.API_ENDPOINT, service_name=self.SERVICE_NAME, diff --git a/stream/tests/test_async_client.py b/stream/tests/test_async_client.py index d4b0c0f..9c4bc2f 100644 --- a/stream/tests/test_async_client.py +++ b/stream/tests/test_async_client.py @@ -1147,6 +1147,93 @@ async def test_reaction_filter(async_client): _first_result_should_be(r, reaction_comment) +@pytest.mark.asyncio +async def test_reaction_add_with_moderation_template(async_client): + """Test adding a reaction with moderation template""" + try: + response = await async_client.reactions.add( + "like", + "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", + "mike", + moderation_template="test_moderation_template", + ) + # If moderation is enabled, verify the reaction was created + assert "id" in response + reaction = await async_client.reactions.get(response["id"]) + assert reaction["kind"] == "like" + assert reaction["user_id"] == "mike" + except Exception as e: + # If moderation is not enabled, we expect a specific error + # The important thing is that the moderation_template parameter + # was accepted and passed to the API without causing a client-side error + error_message = str(e) + assert ( + "moderation not enabled" in error_message + ), f"Expected moderation error, but got: {error_message}" + + +@pytest.mark.asyncio +async def test_reaction_add_child_with_moderation_template(async_client): + """Test adding a child reaction with moderation template""" + # First create a parent reaction + parent_response = await async_client.reactions.add( + "like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike" + ) + + try: + # Add child with moderation template + child_response = await async_client.reactions.add_child( + "reply", + parent_response["id"], + "rob", + data={"text": "Great post!"}, + moderation_template="child_moderation_template", + ) + # If moderation is enabled, verify the child reaction was created + assert "id" in child_response + child_reaction = await async_client.reactions.get(child_response["id"]) + assert child_reaction["kind"] == "reply" + assert child_reaction["user_id"] == "rob" + assert child_reaction["parent"] == parent_response["id"] + except Exception as e: + # If moderation is not enabled, we expect a specific error + # The important thing is that the moderation_template parameter + # was accepted and passed to the API without causing a client-side error + error_message = str(e) + assert ( + "moderation not enabled" in error_message + ), f"Expected moderation error, but got: {error_message}" + + +@pytest.mark.asyncio +async def test_reaction_add_without_moderation_template_backwards_compatibility( + async_client, +): + """Test that existing functionality still works without moderation template""" + response = await async_client.reactions.add( + "like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike" + ) + assert "id" in response + reaction = await async_client.reactions.get(response["id"]) + assert reaction["kind"] == "like" + + +@pytest.mark.asyncio +async def test_reaction_add_child_without_moderation_template_backwards_compatibility( + async_client, +): + """Test that existing child functionality still works without moderation template""" + parent_response = await async_client.reactions.add( + "like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike" + ) + child_response = await async_client.reactions.add_child( + "reply", parent_response["id"], "rob" + ) + assert "id" in child_response + child_reaction = await async_client.reactions.get(child_response["id"]) + assert child_reaction["parent"] == parent_response["id"] + + @pytest.mark.asyncio async def test_user_add(async_client): await async_client.users.add(str(uuid1())) diff --git a/stream/tests/test_client.py b/stream/tests/test_client.py index 3ccb8cc..f30171b 100644 --- a/stream/tests/test_client.py +++ b/stream/tests/test_client.py @@ -1476,6 +1476,83 @@ def test_reaction_add_child(self): ) self.c.reactions.add_child("like", response["id"], "rob") + def test_reaction_add_with_moderation_template(self): + """Test adding a reaction with moderation template""" + try: + response = self.c.reactions.add( + "like", + "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", + "mike", + moderation_template="test_moderation_template", + ) + # If moderation is enabled, verify the reaction was created + self.assertTrue("id" in response) + reaction = self.c.reactions.get(response["id"]) + self.assertEqual(reaction["kind"], "like") + self.assertEqual(reaction["user_id"], "mike") + except Exception as e: + # If moderation is not enabled, we expect a specific error + # The important thing is that the moderation_template parameter + # was accepted and passed to the API without causing a client-side error + error_message = str(e) + self.assertTrue( + "moderation not enabled" in error_message, + f"Expected moderation error, but got: {error_message}", + ) + + def test_reaction_add_child_with_moderation_template(self): + """Test adding a child reaction with moderation template""" + # First create a parent reaction + parent_response = self.c.reactions.add( + "like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike" + ) + + try: + # Add child with moderation template + child_response = self.c.reactions.add_child( + "reply", + parent_response["id"], + "rob", + data={"text": "Great post!"}, + moderation_template="child_moderation_template", + ) + # If moderation is enabled, verify the child reaction was created + self.assertTrue("id" in child_response) + child_reaction = self.c.reactions.get(child_response["id"]) + self.assertEqual(child_reaction["kind"], "reply") + self.assertEqual(child_reaction["user_id"], "rob") + self.assertEqual(child_reaction["parent"], parent_response["id"]) + except Exception as e: + # If moderation is not enabled, we expect a specific error + # The important thing is that the moderation_template parameter + # was accepted and passed to the API without causing a client-side error + error_message = str(e) + self.assertTrue( + "moderation not enabled" in error_message, + f"Expected moderation error, but got: {error_message}", + ) + + def test_reaction_add_without_moderation_template(self): + """Test that existing functionality still works without moderation template""" + response = self.c.reactions.add( + "like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike" + ) + self.assertTrue("id" in response) + reaction = self.c.reactions.get(response["id"]) + self.assertEqual(reaction["kind"], "like") + + def test_reaction_add_child_without_moderation_template(self): + """Test that existing child functionality still works without moderation template""" + parent_response = self.c.reactions.add( + "like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike" + ) + child_response = self.c.reactions.add_child( + "reply", parent_response["id"], "rob" + ) + self.assertTrue("id" in child_response) + child_reaction = self.c.reactions.get(child_response["id"]) + self.assertEqual(child_reaction["parent"], parent_response["id"]) + def test_reaction_filter_random(self): self.c.reactions.filter( kind="like",