8000 Add support for soft deletions for reacitons (#145) · GetStream/stream-python@3fcfb59 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3fcfb59

Browse files
Add support for soft deletions for reacitons (#145)
* implement support for soft deletion of reactions * add tests for soft deletion * drop support for 3.7, add support for 3.12, drop lint commit msg * fix missing ' * add support for STREAM_REGION * make tests work with region * bump deps for python 3.12 * remove yassine from CODEOWNERS
1 parent 53ed956 commit 3fcfb59

File tree

8 files changed

+109
-19
lines changed

8 files changed

+109
-19
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* @JimmyPettersson85 @xernobyl @yaziine
1+
* @JimmyPettersson85 @xernobyl

.github/workflows/ci.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,12 @@ jobs:
1616
strategy:
1717
max-parallel: 1
1818
matrix:
19-
python: ['3.7', '3.8', '3.9', '3.10', '3.11']
19+
python: ['3.8', '3.9', '3.10', '3.11', '3.12']
2020
steps:
2121
- uses: actions/checkout@v3
2222
with:
2323
fetch-depth: 0 # gives the commit linter access to previous commits
2424

25-
- name: Commit message linter
26-
if: ${{ matrix.python == '3.7' }}
27-
uses: wagoid/commitlint-github-action@v4
28-
2925
- uses: actions/setup-python@v3
3026
with:
3127
python-version: ${{ matrix.python }}
@@ -34,7 +30,7 @@ jobs:
3430
run: pip install -q ".[test, ci]"
3531

3632
- name: Lint with ${{ matrix.python }}
37-
if: ${{ matrix.python == '3.7' }}
33+
if: ${{ matrix.python == '3.8' }}
3834
run: make lint
3935

4036
- name: Install, test and code coverage with ${{ matrix.python }}

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
from stream import __version__, __maintainer__, __email__, __license__
66

77
install_requires = [
8-
"requests>=2.28.0,<3",
9-
"pyjwt>=2.6.0,<3",
10-
"pytz>=2022.7.1",
11-
"aiohttp>=3.8.4",
8+
"requests>=2.31.0,<3",
9+
"pyjwt>=2.8.0,<3",
10+
"pytz>=2023.3.post1",
11+
"aiohttp>=3.9.0b0",
1212
]
1313
tests_require = ["pytest", "pytest-cov", "python-dateutil", "pytest-asyncio"]
1414
ci_require = ["black", "flake8", "pytest-cov"]

stream/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ def connect(
3131
"""
3232
from stream.client import AsyncStreamClient, StreamClient
3333

34+
if location is None:
35+
location = os.environ.get("STREAM_REGION")
36+
3437
stream_url = os.environ.get("STREAM_URL")
3538
# support for the heroku STREAM_URL syntax
3639
if stream_url and not api_key:

stream/reactions/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ def update(self, reaction_id, data=None, target_feeds=None):
2323
pass
2424

2525
@abstractmethod
26-
def delete(self, reaction_id):
26+
def delete(self, reaction_id, soft=False):
27+
pass
28+
29+
@abstractmethod
30+
def restore(self, reaction_id):
2731
pass
2832

2933
@abstractmethod

stream/reactions/reaction.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,18 @@ def update(self, reaction_id, data=None, target_feeds=None):
4242
data=payload,
4343
)
4444

45-
def delete(self, reaction_id):
45+
def delete(self, reaction_id, soft=False):
4646
url = f"{self.API_ENDPOINT}{reaction_id}"
4747
return self.client.delete(
48+
url,
49+
service_name=self.SERVICE_NAME,
50+
signature=self.token,
51+
params={"soft": soft},
52+
)
53+
54+
def restore(self, reaction_id):
55+
url = f"{self.API_ENDPOINT}{reaction_id}/restore"
56+
return self.client.put(
4857
url, service_name=self.SERVICE_NAME, signature=self.token
4958
)
5059

@@ -123,9 +132,18 @@ async def update(self, reaction_id, data=None, target_feeds=None):
123132
data=payload,
124133
)
125134

126-
async def delete(self, reaction_id):
135+
async def delete(self, reaction_id, soft=False):
127136
url = f"{self.API_ENDPOINT}{reaction_id}"
128137
return await self.client.delete(
138+
url,
139+
service_name=self.SERVICE_NAME,
140+
signature=self.token,
141+
params={"soft": soft},
142+
)
143+
144+
async def restore(self, reaction_id):
145+
url = f"{self.API_ENDPOINT}{reaction_id}/restore"
146+
return await self.client.put(
129147
url, service_name=self.SERVICE_NAME, signature=self.token
130148
)
131149

stream/tests/test_async_client.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from dateutil.tz import tzlocal
99

1010
import stream
11-
from stream.exceptions import ApiKeyException, InputException
11+
from stream.exceptions import ApiKeyException, InputException, DoesNotExistException
1212

1313

1414
def assert_first_activity_id_equal(activities, correct_activity_id):
@@ -1049,6 +1049,44 @@ async def test_reaction_delete(async_client):
10491049
await async_client.reactions.delete(response["id"])
10501050

10511051

1052+
@pytest.mark.asyncio
1053+
async def test_reaction_hard_delete(async_client):
1054+
response = await async_client.reactions.add(
1055+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1056+
)
1057+
await async_client.reactions.delete(response["id"], soft=False)
1058+
1059+
1060+
@pytest.mark.asyncio
1061+
async def test_reaction_soft_delete(async_client):
1062+
response = await async_client.reactions.add(
1063+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1064+
)
1065+
await async_client.reactions.delete(response["id"], soft=True)
1066+
1067+
1068+
@pytest.mark.asyncio
1069+
async def test_reaction_soft_delete_and_restore(async_client):
1070+
response = await async_client.reactions.add(
1071+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1072+
)
1073+
await async_client.reactions.delete(response["id"], soft=True)
1074+
r1 = await async_client.reactions.get(response["id"])
1075+
assert r1.get("deleted_at", None) is not None
1076+
await async_client.reactions.restore(response["id"])
1077+
r1 = await async_client.reactions.get(response["id"])
1078+
assert "deleted_at" not in r1
1079+
1080+
1081+
@pytest.mark.asyncio
1082+
async def test_reaction_invalid_restore(async_client):
1083+
response = await async_client.reactions.add(
1084+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1085+
)
1086+
with pytest.raises(DoesNotExistException):
1087+
await async_client.reactions.restore(response["id"])
1088+
1089+
10521090
@pytest.mark.asyncio
10531091
async def test_reaction_add_child(async_client):
10541092
response = await async_client.reactions.add(

stream/tests/test_client.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import stream
1919
from stream import serializer
20-
from stream.exceptions import ApiKeyException, InputException
20+
from stream.exceptions import ApiKeyException, InputException, DoesNotExistException
2121
from stream.feed import Feed
2222

2323

@@ -150,14 +150,14 @@ def test_api_url(self):
150150
)
151151

152152
def test_collections_url_default(self):
153-
c = stream.connect("key", "secret")
153+
c = stream.connect("key", "secret", location="")
154154
feed_url = c.get_full_url(relative_url="meta/", service_name="api")
155155

156156
if not self.local_tests:
157157
self.assertEqual(feed_url, "https://api.stream-io-api.com/api/v1.0/meta/")
158158

159159
def test_personalization_url_default(self):
160-
c = stream.connect("key", "secret")
160+
c = stream.connect("key", "secret", location="")
161161
feed_url = c.get_full_url(
162162
relative_url="recommended", service_name="personalization"
163163
)
@@ -169,7 +169,7 @@ def test_personalization_url_default(self):
169169
)
170170

171171
def test_api_url_default(self):
172-
c = stream.connect("key", "secret")
172+
c = stream.connect("key", "secret", location="")
173173
feed_url = c.get_full_url(service_name="api", relative_url="feed/")
174174

175175
if not self.local_tests:
@@ -1439,6 +1439,37 @@ def test_reaction_delete(self):
14391439
)
14401440
self.c.reactions.delete(response["id"])
14411441

1442+
def test_reaction_hard_delete(self):
1443+
response = self.c.reactions.add(
1444+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
< 2851 /code>1445+
)
1446+
self.c.reactions.delete(response["id"], soft=False)
1447+
1448+
def test_reaction_soft_delete(self):
1449+
response = self.c.reactions.add(
1450+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1451+
)
1452+
self.c.reactions.delete(response["id"], soft=True)
1453+
1454+
def test_reaction_soft_delete_and_restore(self):
1455+
response = self.c.reactions.add(
1456+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1457+
)
1458+
self.c.reactions.delete(response["id"], soft=True)
1459+
r1 = self.c.reactions.get(response["id"])
1460+
self.assertIsNot(r1["deleted_at"], None)
1461+
self.c.reactions.restore(response["id"])
1462+
r1 = self.c.reactions.get(response["id"])
1463+
self.assertTrue("deleted_at" not in r1)
1464+
1465+
def test_reaction_invalid_restore(self):
1466+
response = self.c.reactions.add(
1467+
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
1468+
)
1469+
self.assertRaises(
1470+
DoesNotExistException, lambda: self.c.reactions.restore(response["id"])
1471+
)
1472+
14421473
def test_reaction_add_child(self):
14431474
response = self.c.reactions.add(
14441475
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"

0 commit comments

Comments
 (0)
0