Skip to content

Commit b93dd51

Browse files
[FEEDS-791] Support moderating reactions (#150)
* WIP * add some tests * lint fix
1 parent 9c8b9cc commit b93dd51

File tree

5 files changed

+178
-8
lines changed

5 files changed

+178
-8
lines changed

stream/exceptions.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ def __unicode__(self):
1515

1616

1717
class ApiKeyException(StreamApiException):
18-
1918
"""
2019
Raised when there is an issue with your Access Key
2120
"""
@@ -25,7 +24,6 @@ class ApiKeyException(StreamApiException):
2524

2625

2726
class SignatureException(StreamApiException):
28-
2927
"""
3028
Raised when there is an issue with the signature you provided
3129
"""
@@ -35,7 +33,6 @@ class SignatureException(StreamApiException):
3533

3634

3735
class InputException(StreamApiException):
38-
3936
"""
4037
Raised when you send the wrong data to the API
4138
"""
@@ -45,7 +42,6 @@ class InputException(StreamApiException):
4542

4643

4744
class CustomFieldException(StreamApiException):
48-
4945
"""
5046
Raised when there are missing or misconfigured custom fields
5147
"""
@@ -55,7 +51,6 @@ class CustomFieldException(StreamApiException):
5551

5652

5753
class FeedConfigException(StreamApiException):
58-
5954
"""
6055
Raised when there are missing or misconfigured custom fields
6156
"""
@@ -65,7 +60,6 @@ class FeedConfigException(StreamApiException):
6560

6661

6762
class SiteSuspendedException(StreamApiException):
68-
6963
"""
7064
Raised when the site requesting the data is suspended
7165
"""
@@ -75,7 +69,6 @@ class SiteSuspendedException(StreamApiException):
7569

7670

7771
class InvalidPaginationException(StreamApiException):
78-
7972
"""
8073
Raised when there is an issue with your Access Key
8174
"""
@@ -108,7 +101,6 @@ class RankingException(FeedConfigException):
108101

109102

110103
class RateLimitReached(StreamApiException):
111-
112104
"""
113105
Raised when too many requests are performed
114106
"""

stream/reactions/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def add(
1111
data=None,
1212
target_feeds=None,
1313
target_feeds_extra_data=None,
14+
moderation_template=None,
1415
):
1516
pass
1617

@@ -39,6 +40,7 @@ def add_child(
3940
data=None,
4041
target_feeds=None,
4142
target_feeds_extra_data=None,
43+
moderation_template=None,
4244
):
4345
pass
4446

stream/reactions/reaction.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def add(
1010
data=None,
1111
target_feeds=None,
1212
target_feeds_extra_data=None,
13+
moderation_template=None,
1314
):
1415
payload = dict(
1516
kind=kind,
@@ -19,6 +20,8 @@ def add(
1920
target_feeds_extra_data=target_feeds_extra_data,
2021
user_id=user_id,
2122
)
23+
if moderation_template is not None:
24+
payload["moderation_template"] = moderation_template
2225
return self.client.post(
2326
self.API_ENDPOINT,
2427
service_name=self.SERVICE_NAME,
@@ -65,6 +68,7 @@ def add_child(
6568
data=None,
6669
target_feeds=None,
6770
target_feeds_extra_data=None,
71+
moderation_template=None,
6872
):
6973
payload = dict(
7074
kind=kind,
@@ -74,6 +78,8 @@ def add_child(
7478
target_feeds_extra_data=target_feeds_extra_data,
7579
user_id=user_id,
7680
)
81+
if moderation_template is not None:
82+
payload["moderation_template"] = moderation_template
7783
return self.client.post(
7884
self.API_ENDPOINT,
7985
service_name=self.SERVICE_NAME,
@@ -100,6 +106,7 @@ async def add(
100106
data=None,
101107
target_feeds=None,
102108
target_feeds_extra_data=None,
109+
moderation_template=None,
103110
):
104111
payload = dict(
105112
kind=kind,
@@ -109,6 +116,8 @@ async def add(
109116
target_feeds_extra_data=target_feeds_extra_data,
110117
user_id=user_id,
111118
)
119+
if moderation_template is not None:
120+
payload["moderation_template"] = moderation_template
112121
return await self.client.post(
113122
self.API_ENDPOINT,
114123
service_name=self.SERVICE_NAME,
@@ -155,6 +164,7 @@ async def add_child(
155164
data=None,
156165
target_feeds=None,
157166
target_feeds_extra_data=None,
167+
moderation_template=None,
158168
):
159169
payload = dict(
160170
kind=kind,
@@ -164,6 +174,8 @@ async def add_child(
164174
target_feeds_extra_data=target_feeds_extra_data,
165175
user_id=user_id,
166176
)
177+
if moderation_template is not None:
178+
payload["moderation_template"] = moderation_template
167179
return await self.client.post(
168180
self.API_ENDPOINT,
169181
service_name=self.SERVICE_NAME,

stream/tests/test_async_client.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,93 @@ async def test_reaction_filter(async_client):
11471147
_first_result_should_be(r, reaction_comment)
11481148

11491149

1150+
@pytest.mark.asyncio
1151+
async def test_reaction_add_with_moderation_template(async_client):
1152+
"""Test adding a reaction with moderation template"""
1153+
try:
1154+
response = await async_client.reactions.add(
1155+
"like",
1156+
"54a60c1e-4ee3-494b-a1e3-50c06acb5ed4",
1157+
"mike",
1158+
moderation_template="test_moderation_template",
1159+
)
1160+
# If moderation is enabled, verify the reaction was created
1161+
assert "id" in response
1162+
reaction = await async_client.reactions.get(response["id"])
1163+
assert reaction["kind"] == "like"
1164+
assert reaction["user_id"] == "mike"
1165+
except Exception as e:
1166+
# If moderation is not enabled, we expect a specific error
1167+
# The important thing is that the moderation_template parameter
1168+
# was accepted and passed to the API without causing a client-side error
1169+
error_message = str(e)
1170+
assert (
1171+
"moderation not enabled" in error_message
1172+
), f"Expected moderation error, but got: {error_message}"
1173+
1174+
1175+
@pytest.mark.asyncio
1176+
async def test_reaction_add_child_with_moderation_template(async_client):
1177+
"""Test adding a child reaction with moderation template"""
1178+
# First create a parent reaction
1179+
parent_response = await async_client.reactions.add(
1180+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1181+
)
1182+
1183+
try:
1184+
# Add child with moderation template
1185+
child_response = await async_client.reactions.add_child(
1186+
"reply",
1187+
parent_response["id"],
1188+
"rob",
1189+
data={"text": "Great post!"},
1190+
moderation_template="child_moderation_template",
1191+
)
1192+
# If moderation is enabled, verify the child reaction was created
1193+
assert "id" in child_response
1194+
child_reaction = await async_client.reactions.get(child_response["id"])
1195+
assert child_reaction["kind"] == "reply"
1196+
assert child_reaction["user_id"] == "rob"
1197+
assert child_reaction["parent"] == parent_response["id"]
1198+
except Exception as e:
1199+
# If moderation is not enabled, we expect a specific error
1200+
# The important thing is that the moderation_template parameter
1201+
# was accepted and passed to the API without causing a client-side error
1202+
error_message = str(e)
1203+
assert (
1204+
"moderation not enabled" in error_message
1205+
), f"Expected moderation error, but got: {error_message}"
1206+
1207+
1208+
@pytest.mark.asyncio
1209+
async def test_reaction_add_without_moderation_template_backwards_compatibility(
1210+
async_client,
1211+
):
1212+
"""Test that existing functionality still works without moderation template"""
1213+
response = await async_client.reactions.add(
1214+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1215+
)
1216+
assert "id" in response
1217+
reaction = await async_client.reactions.get(response["id"])
1218+
assert reaction["kind"] == "like"
1219+
1220+
1221+
@pytest.mark.asyncio
1222+
async def test_reaction_add_child_without_moderation_template_backwards_compatibility(
1223+
async_client,
1224+
):
1225+
"""Test that existing child functionality still works without moderation template"""
1226+
parent_response = await async_client.reactions.add(
1227+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1228+
)
1229+
child_response = await async_client.reactions.add_child(
1230+
"reply", parent_response["id"], "rob"
1231+
)
1232+
assert "id" in child_response
1233+
child_reaction = await async_client.reactions.get(child_response["id"])
1234+
assert child_reaction["parent"] == parent_response["id"]
1235+
1236+
11501237
@pytest.mark.asyncio
11511238
async def test_user_add(async_client):
11521239
await async_client.users.add(str(uuid1()))

stream/tests/test_client.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,83 @@ def test_reaction_add_child(self):
14761476
)
14771477
self.c.reactions.add_child("like", response["id"], "rob")
14781478

1479+
def test_reaction_add_with_moderation_template(self):
1480+
"""Test adding a reaction with moderation template"""
1481+
try:
1482+
response = self.c.reactions.add(
1483+
"like",
1484+
"54a60c1e-4ee3-494b-a1e3-50c06acb5ed4",
1485+
"mike",
1486+
moderation_template="test_moderation_template",
1487+
)
1488+
# If moderation is enabled, verify the reaction was created
1489+
self.assertTrue("id" in response)
1490+
reaction = self.c.reactions.get(response["id"])
1491+
self.assertEqual(reaction["kind"], "like")
1492+
self.assertEqual(reaction["user_id"], "mike")
1493+
except Exception as e:
1494+
# If moderation is not enabled, we expect a specific error
1495+
# The important thing is that the moderation_template parameter
1496+
# was accepted and passed to the API without causing a client-side error
1497+
error_message = str(e)
1498+
self.assertTrue(
1499+
"moderation not enabled" in error_message,
1500+
f"Expected moderation error, but got: {error_message}",
1501+
)
1502+
1503+
def test_reaction_add_child_with_moderation_template(self):
1504+
"""Test adding a child reaction with moderation template"""
1505+
# First create a parent reaction
1506+
parent_response = self.c.reactions.add(
1507+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1508+
)
1509+
1510+
try:
1511+
# Add child with moderation template
1512+
child_response = self.c.reactions.add_child(
1513+
"reply",
1514+
parent_response["id"],
1515+
"rob",
1516+
data={"text": "Great post!"},
1517+
moderation_template="child_moderation_template",
1518+
)
1519+
# If moderation is enabled, verify the child reaction was created
1520+
self.assertTrue("id" in child_response)
1521+
child_reaction = self.c.reactions.get(child_response["id"])
1522+
self.assertEqual(child_reaction["kind"], "reply")
1523+
self.assertEqual(child_reaction["user_id"], "rob")
1524+
self.assertEqual(child_reaction["parent"], parent_response["id"])
1525+
except Exception as e:
1526+
# If moderation is not enabled, we expect a specific error
1527+
# The important thing is that the moderation_template parameter
1528+
# was accepted and passed to the API without causing a client-side error
1529+
error_message = str(e)
1530+
self.assertTrue(
1531+
"moderation not enabled" in error_message,
1532+
f"Expected moderation error, but got: {error_message}",
1533+
)
1534+
1535+
def test_reaction_add_without_moderation_template(self):
1536+
"""Test that existing functionality still works without moderation template"""
1537+
response = self.c.reactions.add(
1538+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1539+
)
1540+
self.assertTrue("id" in response)
1541+
reaction = self.c.reactions.get(response["id"])
1542+
self.assertEqual(reaction["kind"], "like")
1543+
1544+
def test_reaction_add_child_without_moderation_template(self):
1545+
"""Test that existing child functionality still works without moderation template"""
1546+
parent_response = self.c.reactions.add(
1547+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1548+
)
1549+
child_response = self.c.reactions.add_child(
1550+
"reply", parent_response["id"], "rob"
1551+
)
1552+
self.assertTrue("id" in child_response)
1553+
child_reaction = self.c.reactions.get(child_response["id"])
1554+
self.assertEqual(child_reaction["parent"], parent_response["id"])
1555+
14791556
def test_reaction_filter_random(self):
14801557
self.c.reactions.filter(
14811558
kind="like",

0 commit comments

Comments
 (0)