Skip to content

Commit 7858d0b

Browse files
committed
Add enable/disable for notifications subscriber
1 parent 5e6141e commit 7858d0b

7 files changed

Lines changed: 181 additions & 30 deletions

File tree

mpt_api_client/http/mixins.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,46 @@ async def get(self, resource_id: str, select: list[str] | str | None = None) ->
395395
return await self._resource_action(resource_id=resource_id, query_params={"select": select}) # type: ignore[attr-defined, no-any-return]
396396

397397

398+
class AsyncEnableMixin[Model: BaseModel]:
399+
"""Enable resource mixin."""
400+
401+
async def enable(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
402+
"""Disable a specific resource using."""
403+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
404+
resource_id=resource_id, method="POST", action="enable", json=resource_data
405+
)
406+
407+
408+
class EnableMixin[Model: BaseModel]:
409+
"""Disable resource mixin."""
410+
411+
def enable(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
412+
"""Disable a specific resource using."""
413+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
414+
resource_id=resource_id, method="POST", action="enable", json=resource_data
415+
)
416+
417+
418+
class AsyncDisableMixin[Model: BaseModel]:
419+
"""Disable resource mixin."""
420+
421+
async def disable(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
422+
"""Disable a specific resource using."""
423+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
424+
resource_id=resource_id, method="POST", action="disable", json=resource_data
425+
)
426+
427+
428+
class DisableMixin[Model: BaseModel]:
429+
"""Disable resource mixin."""
430+
431+
def disable(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
432+
"""Disable a specific resource using."""
433+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
434+
resource_id=resource_id, method="POST", action="disable", json=resource_data
435+
)
436+
437+
398438
class QueryableMixin:
399439
"""Mixin providing query functionality for filtering, ordering, and selecting fields."""
400440

mpt_api_client/resources/notifications/subscribers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
from mpt_api_client.http import AsyncService, Service
22
from mpt_api_client.http.mixins import (
33
AsyncCollectionMixin,
4+
AsyncDisableMixin,
5+
AsyncEnableMixin,
46
AsyncManagedResourceMixin,
57
CollectionMixin,
8+
DisableMixin,
9+
EnableMixin,
610
ManagedResourceMixin,
711
)
812
from mpt_api_client.models import Model
@@ -21,6 +25,8 @@ class SubscribersServiceConfig:
2125

2226

2327
class SubscribersService(
28+
EnableMixin[Subscriber],
29+
DisableMixin[Subscriber],
2430
ManagedResourceMixin[Subscriber],
2531
CollectionMixin[Subscriber],
2632
Service[Subscriber],
@@ -30,6 +36,8 @@ class SubscribersService(
3036

3137

3238
class AsyncSubscribersService(
39+
AsyncEnableMixin[Subscriber],
40+
AsyncDisableMixin[Subscriber],
3341
AsyncManagedResourceMixin[Subscriber],
3442
AsyncCollectionMixin[Subscriber],
3543
AsyncService[Subscriber],

tests/e2e/conftest.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,7 @@ def authorization_id(e2e_config):
152152
def price_list_id(e2e_config):
153153
return e2e_config["catalog.price_list.id"]
154154

155+
155156
@pytest.fixture
156157
def user_group_id(e2e_config):
157158
return e2e_config["accounts.user_group.id"]
158-
159-
160-
@pytest.fixture
161-
def account_id(e2e_config):
162-
return e2e_config["accounts.account.id"]

tests/e2e/notifications/subscribers/conftest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import pytest
22

3+
from mpt_api_client.exceptions import MPTAPIError
4+
35

46
@pytest.fixture
57
def subscriber_id(e2e_config):
@@ -37,3 +39,26 @@ def _subscriber( # noqa: WPS430
3739
}
3840

3941
return _subscriber
42+
43+
44+
@pytest.fixture
45+
def disabled_subscriber_id(mpt_client, subscriber_id):
46+
subscriber = mpt_client.notifications.subscribers.get(subscriber_id)
47+
if subscriber.status != "Disabled":
48+
subscriber = mpt_client.notifications.subscribers.disable(subscriber_id)
49+
50+
yield subscriber.id
51+
52+
try:
53+
mpt_client.notifications.subscribers.enable(subscriber_id)
54+
except MPTAPIError:
55+
print(f"TEARDOWN - Unable to re-enable subscriber {subscriber_id=}") # noqa: WPS421
56+
57+
58+
@pytest.fixture
59+
def active_subscriber_id(mpt_client, subscriber_id):
60+
subscriber = mpt_client.notifications.subscribers.get(subscriber_id)
61+
if subscriber.status != "Active":
62+
subscriber = mpt_client.notifications.subscribers.enable(subscriber_id)
63+
64+
return subscriber.id

tests/e2e/notifications/subscribers/test_async_subscribers.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,29 @@ async def test_update_subscriber_not_found(
4949
)
5050

5151

52-
@pytest.mark.skip(reason="`disable` action not implemented") # TODO: remove after implementing
53-
async def test_disable_subscriber(async_mpt_client, subscriber_id):
54-
result = await async_mpt_client.notifications.subscribers.disable(subscriber_id)
52+
async def test_disable_subscriber(async_mpt_client, active_subscriber_id):
53+
result = await async_mpt_client.notifications.subscribers.disable(active_subscriber_id)
5554

5655
assert result is not None
57-
assert result.status == "disabled"
56+
assert result.status == "Disabled"
5857

5958

60-
@pytest.mark.skip(reason="`disable` action not implemented") # TODO: remove after implementing
6159
async def test_disable_subscriber_not_found(async_mpt_client, invalid_subscriber_id):
62-
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
60+
with pytest.raises(
61+
MPTAPIError, match=r"400 Bad Request - Subscriber with id 'NTS-0000-0000-0000' not found"
62+
):
6363
await async_mpt_client.notifications.subscribers.disable(invalid_subscriber_id)
6464

6565

66-
@pytest.mark.skip(reason="`enable` action not implemented") # TODO: remove after implementing
67-
async def test_enable_subscriber(async_mpt_client, subscriber_id):
68-
result = await async_mpt_client.notifications.subscribers.enable(subscriber_id)
66+
async def test_enable_subscriber(async_mpt_client, disabled_subscriber_id):
67+
result = await async_mpt_client.notifications.subscribers.enable(disabled_subscriber_id)
6968

7069
assert result is not None
71-
assert result.status == "enabled"
70+
assert result.status == "Active"
7271

7372

74-
@pytest.mark.skip(reason="`enable` action not implemented") # TODO: remove after implementing
7573
async def test_enable_subscriber_not_found(async_mpt_client, invalid_subscriber_id):
76-
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
74+
with pytest.raises(
75+
MPTAPIError, match=r"400 Bad Request - Subscriber with id 'NTS-0000-0000-0000' not found"
76+
):
7777
await async_mpt_client.notifications.subscribers.enable(invalid_subscriber_id)

tests/e2e/notifications/subscribers/test_sync_subscribers.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,29 +45,29 @@ def test_update_subscriber_not_found(
4545
mpt_ops.notifications.subscribers.update(invalid_subscriber_id, updated_subscriber_data)
4646

4747

48-
@pytest.mark.skip(reason="`disable` action not implemented") # TODO: remove after implementing
49-
def test_disable_subscriber(mpt_client, subscriber_id):
50-
result = mpt_client.notifications.subscribers.disable(subscriber_id)
48+
def test_disable_subscriber(mpt_client, active_subscriber_id):
49+
result = mpt_client.notifications.subscribers.disable(active_subscriber_id)
5150

5251
assert result is not None
53-
assert result.status == "disabled"
52+
assert result.status == "Disabled"
5453

5554

56-
@pytest.mark.skip(reason="`disable` action not implemented") # TODO: remove after implementing
5755
def test_disable_subscriber_not_found(mpt_client, invalid_subscriber_id):
58-
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
56+
with pytest.raises(
57+
MPTAPIError, match=r"400 Bad Request - Subscriber with id 'NTS-0000-0000-0000' not found"
58+
):
5959
mpt_client.notifications.subscribers.disable(invalid_subscriber_id)
6060

6161

62-
@pytest.mark.skip(reason="`enable` action not implemented") # TODO: remove after implementing
63-
def test_enable_subscriber(mpt_client, subscriber_id):
64-
result = mpt_client.notifications.subscribers.enable(subscriber_id)
62+
def test_enable_subscriber(mpt_client, disabled_subscriber_id):
63+
result = mpt_client.notifications.subscribers.enable(disabled_subscriber_id)
6564

6665
assert result is not None
67-
assert result.status == "enabled"
66+
assert result.status == "Active"
6867

6968

70-
@pytest.mark.skip(reason="`enable` action not implemented") # TODO: remove after implementing
7169
def test_enable_subscriber_not_found(mpt_client, invalid_subscriber_id):
72-
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
70+
with pytest.raises(
71+
MPTAPIError, match=r"400 Bad Request - Subscriber with id 'NTS-0000-0000-0000' not found"
72+
):
7373
mpt_client.notifications.subscribers.enable(invalid_subscriber_id)

tests/unit/http/test_mixins.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88
from mpt_api_client import RQLQuery
99
from mpt_api_client.exceptions import MPTAPIError
1010
from mpt_api_client.http import AsyncService, Service
11-
from mpt_api_client.http.mixins import (
11+
from mpt_api_client.http.mixins import ( # noqa: WPS235
12+
AsyncDisableMixin,
13+
AsyncEnableMixin,
1214
AsyncFilesOperationsMixin,
1315
AsyncManagedResourceMixin,
1416
AsyncModifiableResourceMixin,
1517
AsyncUpdateFileMixin,
18+
DisableMixin,
19+
EnableMixin,
1620
FilesOperationsMixin,
1721
ManagedResourceMixin,
1822
ModifiableResourceMixin,
@@ -67,6 +71,24 @@ class DummyAsyncUpdateFileService(
6771
_upload_data_key = "document"
6872

6973

74+
class EnableDisableSercice(
75+
EnableMixin[DummyModel],
76+
DisableMixin[DummyModel],
77+
Service[DummyModel],
78+
):
79+
_endpoint = "/public/v1/dummy"
80+
_model_class = DummyModel
81+
82+
83+
class AsyncEnableDisableSercice(
84+
AsyncEnableMixin[DummyModel],
85+
AsyncDisableMixin[DummyModel],
86+
AsyncService[DummyModel],
87+
):
88+
_endpoint = "/public/v1/dummy"
89+
_model_class = DummyModel
90+
91+
7092
@pytest.fixture
7193
def dummy_file_operations_service(http_client):
7294
"""Fixture for DummyFileOperationsService."""
@@ -1277,3 +1299,63 @@ async def test_async_update_file_no_file(async_update_file_service): # noqa: WP
12771299
assert "multipart/form-data" in request.headers["Content-Type"]
12781300
assert result.to_dict() == response_expected_data
12791301
assert isinstance(result, DummyModel)
1302+
1303+
1304+
def test_enable_mixin(http_client):
1305+
service = EnableDisableSercice(http_client=http_client)
1306+
resource_data = {"name": "Test Resource", "status": "active"}
1307+
response = httpx.Response(httpx.codes.OK, json=resource_data)
1308+
with respx.mock:
1309+
mock_route = respx.post("https://api.example.com/public/v1/dummy/RES-123/enable").mock(
1310+
return_value=response
1311+
)
1312+
1313+
result = service.enable("RES-123", {}) # act
1314+
1315+
assert mock_route.call_count == 1
1316+
assert result.to_dict() == resource_data
1317+
1318+
1319+
def test_disable_mixin(http_client):
1320+
service = EnableDisableSercice(http_client=http_client)
1321+
resource_data = {"name": "Test Resource", "status": "disabled"}
1322+
response = httpx.Response(httpx.codes.OK, json=resource_data)
1323+
with respx.mock:
1324+
mock_route = respx.post("https://api.example.com/public/v1/dummy/RES-123/disable").mock(
1325+
return_value=response
1326+
)
1327+
1328+
result = service.disable("RES-123", {}) # act
1329+
1330+
assert mock_route.call_count == 1
1331+
assert result.to_dict() == resource_data
1332+
1333+
1334+
async def test_async_disable_mixin(async_http_client):
1335+
service = AsyncEnableDisableSercice(http_client=async_http_client)
1336+
resource_data = {"name": "Test Resource", "status": "disabled"}
1337+
response = httpx.Response(httpx.codes.OK, json=resource_data)
1338+
with respx.mock:
1339+
mock_route = respx.post("https://api.example.com/public/v1/dummy/RES-123/disable").mock(
1340+
return_value=response
1341+
)
1342+
1343+
result = await service.disable("RES-123", {}) # act
1344+
1345+
assert mock_route.call_count == 1
1346+
assert result.to_dict() == resource_data
1347+
1348+
1349+
async def test_async_enable_mixin(async_http_client):
1350+
service = AsyncEnableDisableSercice(http_client=async_http_client)
1351+
resource_data = {"name": "Test Resource", "status": "active"}
1352+
response = httpx.Response(httpx.codes.OK, json=resource_data)
1353+
with respx.mock:
1354+
mock_route = respx.post("https://api.example.com/public/v1/dummy/RES-123/enable").mock(
1355+
return_value=response
1356+
)
1357+
1358+
result = await service.enable("RES-123", {}) # act
1359+
1360+
assert mock_route.call_count == 1
1361+
assert result.to_dict() == resource_data

0 commit comments

Comments
 (0)