Skip to content

Commit

Permalink
Add support for soft deletions for reacitons (#145)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
JimmyPettersson85 authored Oct 25, 2023
1 parent 53ed956 commit 3fcfb59
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @JimmyPettersson85 @xernobyl @yaziine
* @JimmyPettersson85 @xernobyl
8 changes: 2 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@ jobs:
strategy:
max-parallel: 1
matrix:
python: ['3.7', '3.8', '3.9', '3.10', '3.11']
python: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # gives the commit linter access to previous commits

- name: Commit message linter
if: ${{ matrix.python == '3.7' }}
uses: wagoid/commitlint-github-action@v4

- uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python }}
Expand All @@ -34,7 +30,7 @@ jobs:
run: pip install -q ".[test, ci]"

- name: Lint with ${{ matrix.python }}
if: ${{ matrix.python == '3.7' }}
if: ${{ matrix.python == '3.8' }}
run: make lint

- name: Install, test and code coverage with ${{ matrix.python }}
Expand Down
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from stream import __version__, __maintainer__, __email__, __license__

install_requires = [
"requests>=2.28.0,<3",
"pyjwt>=2.6.0,<3",
"pytz>=2022.7.1",
"aiohttp>=3.8.4",
"requests>=2.31.0,<3",
"pyjwt>=2.8.0,<3",
"pytz>=2023.3.post1",
"aiohttp>=3.9.0b0",
]
tests_require = ["pytest", "pytest-cov", "python-dateutil", "pytest-asyncio"]
ci_require = ["black", "flake8", "pytest-cov"]
Expand Down
3 changes: 3 additions & 0 deletions stream/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ def connect(
"""
from stream.client import AsyncStreamClient, StreamClient

if location is None:
location = os.environ.get("STREAM_REGION")

stream_url = os.environ.get("STREAM_URL")
# support for the heroku STREAM_URL syntax
if stream_url and not api_key:
Expand Down
6 changes: 5 additions & 1 deletion stream/reactions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ def update(self, reaction_id, data=None, target_feeds=None):
pass

@abstractmethod
def delete(self, reaction_id):
def delete(self, reaction_id, soft=False):
pass

@abstractmethod
def restore(self, reaction_id):
pass

@abstractmethod
Expand Down
22 changes: 20 additions & 2 deletions stream/reactions/reaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,18 @@ def update(self, reaction_id, data=None, target_feeds=None):
data=payload,
)

def delete(self, reaction_id):
def delete(self, reaction_id, soft=False):
url = f"{self.API_ENDPOINT}{reaction_id}"
return self.client.delete(
url,
service_name=self.SERVICE_NAME,
signature=self.token,
params={"soft": soft},
)

def restore(self, reaction_id):
url = f"{self.API_ENDPOINT}{reaction_id}/restore"
return self.client.put(
url, service_name=self.SERVICE_NAME, signature=self.token
)

Expand Down Expand Up @@ -123,9 +132,18 @@ async def update(self, reaction_id, data=None, target_feeds=None):
data=payload,
)

async def delete(self, reaction_id):
async def delete(self, reaction_id, soft=False):
url = f"{self.API_ENDPOINT}{reaction_id}"
return await self.client.delete(
url,
service_name=self.SERVICE_NAME,
signature=self.token,
params={"soft": soft},
)

async def restore(self, reaction_id):
url = f"{self.API_ENDPOINT}{reaction_id}/restore"
return await self.client.put(
url, service_name=self.SERVICE_NAME, signature=self.token
)

Expand Down
40 changes: 39 additions & 1 deletion stream/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dateutil.tz import tzlocal

import stream
from stream.exceptions import ApiKeyException, InputException
from stream.exceptions import ApiKeyException, InputException, DoesNotExistException


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


@pytest.mark.asyncio
async def test_reaction_hard_delete(async_client):
response = await async_client.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
await async_client.reactions.delete(response["id"], soft=False)


@pytest.mark.asyncio
async def test_reaction_soft_delete(async_client):
response = await async_client.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
await async_client.reactions.delete(response["id"], soft=True)


@pytest.mark.asyncio
async def test_reaction_soft_delete_and_restore(async_client):
response = await async_client.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
await async_client.reactions.delete(response["id"], soft=True)
r1 = await async_client.reactions.get(response["id"])
assert r1.get("deleted_at", None) is not None
await async_client.reactions.restore(response["id"])
r1 = await async_client.reactions.get(response["id"])
assert "deleted_at" not in r1


@pytest.mark.asyncio
async def test_reaction_invalid_restore(async_client):
response = await async_client.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
with pytest.raises(DoesNotExistException):
await async_client.reactions.restore(response["id"])


@pytest.mark.asyncio
async def test_reaction_add_child(async_client):
response = await async_client.reactions.add(
Expand Down
39 changes: 35 additions & 4 deletions stream/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import stream
from stream import serializer
from stream.exceptions import ApiKeyException, InputException
from stream.exceptions import ApiKeyException, InputException, DoesNotExistException
from stream.feed import Feed


Expand Down Expand Up @@ -150,14 +150,14 @@ def test_api_url(self):
)

def test_collections_url_default(self):
c = stream.connect("key", "secret")
c = stream.connect("key", "secret", location="")
feed_url = c.get_full_url(relative_url="meta/", service_name="api")

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

def test_personalization_url_default(self):
c = stream.connect("key", "secret")
c = stream.connect("key", "secret", location="")
feed_url = c.get_full_url(
relative_url="recommended", service_name="personalization"
)
Expand All @@ -169,7 +169,7 @@ def test_personalization_url_default(self):
)

def test_api_url_default(self):
c = stream.connect("key", "secret")
c = stream.connect("key", "secret", location="")
feed_url = c.get_full_url(service_name="api", relative_url="feed/")

if not self.local_tests:
Expand Down Expand Up @@ -1439,6 +1439,37 @@ def test_reaction_delete(self):
)
self.c.reactions.delete(response["id"])

def test_reaction_hard_delete(self):
response = self.c.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
self.c.reactions.delete(response["id"], soft=False)

def test_reaction_soft_delete(self):
response = self.c.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
self.c.reactions.delete(response["id"], soft=True)

def test_reaction_soft_delete_and_restore(self):
response = self.c.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
self.c.reactions.delete(response["id"], soft=True)
r1 = self.c.reactions.get(response["id"])
self.assertIsNot(r1["deleted_at"], None)
self.c.reactions.restore(response["id"])
r1 = self.c.reactions.get(response["id"])
self.assertTrue("deleted_at" not in r1)

def test_reaction_invalid_restore(self):
response = self.c.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
)
self.assertRaises(
DoesNotExistException, lambda: self.c.reactions.restore(response["id"])
)

def test_reaction_add_child(self):
response = self.c.reactions.add(
"like", "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", "mike"
Expand Down

0 comments on commit 3fcfb59

Please sign in to comment.