diff --git a/bring_api/bring.py b/bring_api/bring.py index a37f277..407c618 100644 --- a/bring_api/bring.py +++ b/bring_api/bring.py @@ -8,7 +8,6 @@ import logging import os import time -from uuid import UUID import aiohttp from mashumaro.exceptions import MissingField @@ -64,12 +63,12 @@ def __init__( self.mail = mail self.password = password - self.public_uuid: UUID | None = None + self.public_uuid: str = "" self.user_list_settings: dict[str, dict[str, str]] = {} self.user_locale = BRING_DEFAULT_LOCALE self.__translations: dict[str, dict[str, str]] = {} - self.uuid: UUID | None = None + self.uuid: str = "" self.url = URL(API_BASE_URL) @@ -164,8 +163,8 @@ async def login(self) -> BringAuthResponse: self.uuid = data.uuid self.public_uuid = data.publicUuid - self.headers["X-BRING-USER-UUID"] = str(self.uuid) - self.headers["X-BRING-PUBLIC-USER-UUID"] = str(self.public_uuid) + self.headers["X-BRING-USER-UUID"] = self.uuid + self.headers["X-BRING-PUBLIC-USER-UUID"] = self.public_uuid self.headers["Authorization"] = f"{data.token_type} {data.access_token}" self.refresh_token = data.refresh_token self.expires_in = data.expires_in @@ -220,7 +219,7 @@ async def load_lists(self) -> BringListResponse: """ try: - url = self.url / "bringusers" / str(self.uuid) / "lists" + url = self.url / "bringusers" / self.uuid / "lists" async with self._session.get(url, headers=self.headers) as r: _LOGGER.debug( "Response from %s [%s]: %s", url, r.status, await r.text() @@ -262,7 +261,7 @@ async def load_lists(self) -> BringListResponse: "Loading lists failed due to request exception." ) from e - async def get_list(self, list_uuid: UUID) -> BringItemsResponse: + async def get_list(self, list_uuid: str) -> BringItemsResponse: """Get all items from a shopping list. Parameters @@ -286,7 +285,7 @@ async def get_list(self, list_uuid: UUID) -> BringItemsResponse: """ try: - url = self.url / "v2/bringlists" / str(list_uuid) + url = self.url / "v2/bringlists" / list_uuid async with self._session.get(url, headers=self.headers) as r: _LOGGER.debug( "Response from %s [%s]: %s", url, r.status, await r.text() @@ -438,10 +437,10 @@ async def get_all_item_details( async def save_item( self, - list_uuid: UUID, + list_uuid: str, item_name: str, specification: str = "", - item_uuid: UUID | None = None, + item_uuid: str | None = None, ) -> aiohttp.ClientResponse: """Save an item to a shopping list. @@ -469,11 +468,7 @@ async def save_item( If the request fails. """ - data = BringItem( - itemId=item_name, - spec=specification, - uuid=str(item_uuid) if item_uuid else None, - ) + data = BringItem(itemId=item_name, spec=specification, uuid=item_uuid) try: return await self.batch_update_list(list_uuid, data, BringItemOperation.ADD) except BringRequestException as e: @@ -491,10 +486,10 @@ async def save_item( async def update_item( self, - list_uuid: UUID, + list_uuid: str, item_name: str, specification: str = "", - item_uuid: UUID | None = None, + item_uuid: str | None = None, ) -> aiohttp.ClientResponse: """Update an existing list item. @@ -528,7 +523,7 @@ async def update_item( data = BringItem( itemId=item_name, spec=specification, - uuid=str(item_uuid) if item_uuid else None, + uuid=item_uuid, ) try: return await self.batch_update_list(list_uuid, data, BringItemOperation.ADD) @@ -546,7 +541,7 @@ async def update_item( ) from e async def remove_item( - self, list_uuid: UUID, item_name: str, item_uuid: UUID | None = None + self, list_uuid: str, item_name: str, item_uuid: str | None = None ) -> aiohttp.ClientResponse: """Remove an item from a shopping list. @@ -574,7 +569,7 @@ async def remove_item( data = BringItem( itemId=item_name, spec="", - uuid=str(item_uuid) if item_uuid else None, + uuid=item_uuid, ) try: return await self.batch_update_list( @@ -594,10 +589,10 @@ async def remove_item( async def complete_item( self, - list_uuid: UUID, + list_uuid: str, item_name: str, specification: str = "", - item_uuid: UUID | None = None, + item_uuid: str | None = None, ) -> aiohttp.ClientResponse: """Complete an item from a shopping list. This will add it to recent items. @@ -628,7 +623,7 @@ async def complete_item( data = BringItem( itemId=item_name, spec=specification, - uuid=str(item_uuid) if item_uuid else None, + uuid=item_uuid, ) try: return await self.batch_update_list( @@ -648,7 +643,7 @@ async def complete_item( async def notify( self, - list_uuid: UUID, + list_uuid: str, notification_type: BringNotificationType, item_name: str | None = None, ) -> aiohttp.ClientResponse: @@ -684,7 +679,7 @@ async def notify( json_data = BringNotificationsConfigType( arguments=[], listNotificationType=notification_type.value, - senderPublicUserUuid=str(self.public_uuid), + senderPublicUserUuid=self.public_uuid, ) if not isinstance(notification_type, BringNotificationType): @@ -700,7 +695,7 @@ async def notify( json_data["arguments"] = [item_name] try: - url = self.url / "v2/bringnotifications/lists" / str(list_uuid) + url = self.url / "v2/bringnotifications/lists" / list_uuid async with self._session.post( url, headers=self.headers, json=json_data ) as r: @@ -1018,7 +1013,7 @@ async def get_all_user_settings(self) -> BringUserSettingsResponse: """ try: - url = self.url / "bringusersettings" / str(self.uuid) + url = self.url / "bringusersettings" / self.uuid async with self._session.get(url, headers=self.headers) as r: _LOGGER.debug( "Response from %s [%s]: %s", url, r.status, await r.text() @@ -1075,7 +1070,7 @@ async def get_all_user_settings(self) -> BringUserSettingsResponse: "Loading user settings failed due to request exception." ) from e - def __locale(self, list_uuid: UUID) -> str: + def __locale(self, list_uuid: str) -> str: """Get list or user locale. Returns @@ -1089,8 +1084,8 @@ def __locale(self, list_uuid: UUID) -> str: If list locale could not be determined from the userlistsettings or user. """ - if str(list_uuid) in self.user_list_settings: - return self.user_list_settings[str(list_uuid)].get( + if list_uuid in self.user_list_settings: + return self.user_list_settings[list_uuid].get( "listArticleLanguage", self.user_locale ) return self.user_locale @@ -1146,7 +1141,7 @@ async def get_user_account(self) -> BringSyncCurrentUserResponse: """ try: - url = self.url / "v2/bringusers" / str(self.uuid) + url = self.url / "v2/bringusers" / self.uuid async with self._session.get(url, headers=self.headers) as r: _LOGGER.debug( "Response from %s [%s]: %s", url, r.status, await r.text() @@ -1194,7 +1189,7 @@ async def get_user_account(self) -> BringSyncCurrentUserResponse: async def batch_update_list( self, - list_uuid: UUID, + list_uuid: str, items: BringItem | list[BringItem] | list[dict[str, str]], operation: BringItemOperation | None = None, ) -> aiohttp.ClientResponse: @@ -1255,7 +1250,7 @@ async def batch_update_list( } try: - url = self.url / "v2/bringlists" / str(list_uuid) / "items" + url = self.url / "v2/bringlists" / list_uuid / "items" async with self._session.put( url, headers=self.headers, json=json_data ) as r: @@ -1388,7 +1383,7 @@ async def retrieve_new_access_token( return data async def set_list_article_language( - self, list_uuid: UUID, language: str + self, list_uuid: str, language: str ) -> aiohttp.ClientResponse: """Set the article language for a specified list. @@ -1420,8 +1415,8 @@ async def set_list_article_language( url = ( self.url / "bringusersettings" - / str(self.uuid) - / str(list_uuid) + / self.uuid + / list_uuid / "listArticleLanguage" ) @@ -1461,10 +1456,10 @@ async def set_list_article_language( "Set list article language failed due to request exception." ) from e - async def get_activity(self, list_uuid: UUID) -> BringActivityResponse: + async def get_activity(self, list_uuid: str) -> BringActivityResponse: """Get activity for given list.""" try: - url = self.url / "v2/bringlists" / str(list_uuid) / "activity" + url = self.url / "v2/bringlists" / list_uuid / "activity" async with self._session.get(url, headers=self.headers) as r: _LOGGER.debug( "Response from %s [%s]: %s", url, r.status, await r.text() diff --git a/bring_api/types.py b/bring_api/types.py index dd32002..5b207b4 100644 --- a/bring_api/types.py +++ b/bring_api/types.py @@ -4,7 +4,6 @@ from datetime import datetime from enum import Enum, StrEnum from typing import Literal, NotRequired, TypedDict -from uuid import UUID from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -13,7 +12,7 @@ class BringList(DataClassORJSONMixin): """A list class. Represents a single list.""" - listUuid: UUID + listUuid: str name: str theme: str @@ -39,7 +38,7 @@ class Attribute(DataClassORJSONMixin): class BringPurchase(DataClassORJSONMixin): """A purchase class. Represents a single item.""" - uuid: UUID + uuid: str itemId: str specification: str attributes: list[Attribute] = field(default_factory=list) @@ -53,9 +52,9 @@ class BringListItemDetails(DataClassORJSONMixin): Caution: This does not have to be an item that is currently marked as 'to buy'. """ - uuid: UUID + uuid: str itemId: str - listUuid: UUID + listUuid: str userIconItemId: str userSectionId: str assignedTo: str @@ -66,10 +65,9 @@ class BringListItemDetails(DataClassORJSONMixin): class BringAuthResponse(DataClassORJSONMixin): """An auth response class.""" - uuid: UUID - publicUuid: UUID - - bringListUUID: UUID + uuid: str + publicUuid: str + bringListUUID: str access_token: str refresh_token: str token_type: str @@ -98,7 +96,7 @@ class Items(DataClassORJSONMixin): class BringItemsResponse(DataClassORJSONMixin): """An items response class.""" - uuid: UUID + uuid: str status: str items: Items @@ -184,9 +182,9 @@ class BringSyncCurrentUserResponse(DataClassORJSONMixin): email: str emailVerified: bool premiumConfiguration: dict[str, bool] - publicUserUuid: UUID + publicUserUuid: str userLocale: UserLocale - userUuid: UUID + userUuid: str name: str = "" photoPath: str = "" @@ -247,9 +245,9 @@ class ActivityType(StrEnum): class ActivityContent: """An activity content entry.""" - uuid: UUID + uuid: str sessionDate: datetime - publicUserUuid: UUID + publicUserUuid: str items: list[BringPurchase] = field(default_factory=list) purchase: list[BringPurchase] = field(default_factory=list) recently: list[BringPurchase] = field(default_factory=list) diff --git a/tests/__snapshots__/test_bring.ambr b/tests/__snapshots__/test_bring.ambr index 01da957..0cdfb34 100644 --- a/tests/__snapshots__/test_bring.ambr +++ b/tests/__snapshots__/test_bring.ambr @@ -1,24 +1,24 @@ # serializer version: 1 # name: TestGetActivity.test_get_activity - BringActivityResponse(timeline=[Activity(type=, content=ActivityContent(uuid=UUID('673594a9-f92d-4cb6-adf1-d2f7a83207a4'), sessionDate=datetime.datetime(2025, 1, 1, 3, 9, 33, 36000, tzinfo=datetime.timezone.utc), publicUserUuid=UUID('98615d7e-0a7d-4a7e-8f73-a9cbb9f1bc32'), items=[], purchase=[BringPurchase(uuid=UUID('658a3770-1a03-4ee0-94a6-10362a642377'), itemId='Gurke', specification='', attributes=[])], recently=[BringPurchase(uuid=UUID('1ed22d3d-f19b-4530-a518-19872da3fd3e'), itemId='Milch', specification='', attributes=[])])), Activity(type=, content=ActivityContent(uuid=UUID('9a16635c-dea2-4e00-904a-c5034f9cfecf'), sessionDate=datetime.datetime(2025, 1, 1, 2, 54, 57, 656000, tzinfo=datetime.timezone.utc), publicUserUuid=UUID('6743a171-247d-46d0-bc06-baf31194f949'), items=[BringPurchase(uuid=UUID('66a633a2-ae09-47bf-8845-3c0198480544'), itemId='Joghurt', specification='', attributes=[])], purchase=[], recently=[])), Activity(type=, content=ActivityContent(uuid=UUID('303dedf6-d4b2-4d25-a8cd-1c7967b84fcb'), sessionDate=datetime.datetime(2025, 1, 1, 3, 9, 12, 380000, tzinfo=datetime.timezone.utc), publicUserUuid=UUID('6d79d10b-70b2-443f-9f7e-0b02e670c402'), items=[BringPurchase(uuid=UUID('2ba8ddb6-01c6-4b0b-a89d-f3da6b291528'), itemId='Tofu', specification='', attributes=[])], purchase=[], recently=[]))], timestamp=datetime.datetime(2025, 1, 1, 3, 9, 33, 36000, tzinfo=datetime.timezone.utc), totalEvents=2) + BringActivityResponse(timeline=[Activity(type=, content=ActivityContent(uuid='673594a9-f92d-4cb6-adf1-d2f7a83207a4', sessionDate=datetime.datetime(2025, 1, 1, 3, 9, 33, 36000, tzinfo=datetime.timezone.utc), publicUserUuid='98615d7e-0a7d-4a7e-8f73-a9cbb9f1bc32', items=[], purchase=[BringPurchase(uuid='658a3770-1a03-4ee0-94a6-10362a642377', itemId='Gurke', specification='', attributes=[])], recently=[BringPurchase(uuid='1ed22d3d-f19b-4530-a518-19872da3fd3e', itemId='Milch', specification='', attributes=[])])), Activity(type=, content=ActivityContent(uuid='9a16635c-dea2-4e00-904a-c5034f9cfecf', sessionDate=datetime.datetime(2025, 1, 1, 2, 54, 57, 656000, tzinfo=datetime.timezone.utc), publicUserUuid='6743a171-247d-46d0-bc06-baf31194f949', items=[BringPurchase(uuid='66a633a2-ae09-47bf-8845-3c0198480544', itemId='Joghurt', specification='', attributes=[])], purchase=[], recently=[])), Activity(type=, content=ActivityContent(uuid='303dedf6-d4b2-4d25-a8cd-1c7967b84fcb', sessionDate=datetime.datetime(2025, 1, 1, 3, 9, 12, 380000, tzinfo=datetime.timezone.utc), publicUserUuid='6d79d10b-70b2-443f-9f7e-0b02e670c402', items=[BringPurchase(uuid='2ba8ddb6-01c6-4b0b-a89d-f3da6b291528', itemId='Tofu', specification='', attributes=[])], purchase=[], recently=[]))], timestamp=datetime.datetime(2025, 1, 1, 3, 9, 33, 36000, tzinfo=datetime.timezone.utc), totalEvents=2) # --- # name: TestGetAllItemDetails.test_get_all_item_details - BringListItemsDetailsResponse(items=[BringListItemDetails(uuid=UUID('bfb5634c-d219-4d66-b68e-1388e54f0bb0'), itemId='Milchreis', listUuid=UUID('00000000-0000-0000-0000-000000000000'), userIconItemId='Reis', userSectionId='Getreideprodukte', assignedTo='', imageUrl=''), BringListItemDetails(uuid=UUID('0056b23c-7fc3-44da-8c34-426f8b632220'), itemId='Zitronensaft', listUuid=UUID('00000000-0000-0000-0000-000000000000'), userIconItemId='Zitrone', userSectionId='Zutaten & Gewürze', assignedTo='', imageUrl='')]) + BringListItemsDetailsResponse(items=[BringListItemDetails(uuid='bfb5634c-d219-4d66-b68e-1388e54f0bb0', itemId='Milchreis', listUuid='00000000-0000-0000-0000-000000000000', userIconItemId='Reis', userSectionId='Getreideprodukte', assignedTo='', imageUrl=''), BringListItemDetails(uuid='0056b23c-7fc3-44da-8c34-426f8b632220', itemId='Zitronensaft', listUuid='00000000-0000-0000-0000-000000000000', userIconItemId='Zitrone', userSectionId='Zutaten & Gewürze', assignedTo='', imageUrl='')]) # --- # name: TestGetAllUserSettings.test_get_all_user_settings BringUserSettingsResponse(usersettings=[BringUserSettingsEntry(key='autoPush', value='ON'), BringUserSettingsEntry(key='purchaseStyle', value='grouped'), BringUserSettingsEntry(key='premiumHideSponsoredCategories', value='OFF'), BringUserSettingsEntry(key='premiumHideInspirationsBadge', value='OFF'), BringUserSettingsEntry(key='premiumHideOffersBadge', value='OFF'), BringUserSettingsEntry(key='premiumHideOffersOnMain', value='OFF'), BringUserSettingsEntry(key='defaultListUUID', value='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx'), BringUserSettingsEntry(key='discountActivatorOnMainEnabled', value='OFF'), BringUserSettingsEntry(key='onboardClient', value='android')], userlistsettings=[BringUserListSettingEntry(listUuid='00000000-0000-0000-0000-000000000000', usersettings=[BringUserSettingsEntry(key='listSectionOrder', value='["Früchte & Gemüse","Brot & Gebäck","Milch & Käse","Fleisch & Fisch","Zutaten & Gewürze","Fertig- & Tiefkühlprodukte","Getreideprodukte","Snacks & Süsswaren","Getränke & Tabak","Haushalt & Gesundheit","Pflege & Gesundheit","Tierbedarf","Baumarkt & Garten","Eigene Artikel"]'), BringUserSettingsEntry(key='listArticleLanguage', value='de-DE')])]) # --- # name: TestGetList.test_get_list - BringItemsResponse(uuid=UUID('00000000-0000-0000-0000-000000000000'), status='SHARED', items=Items(purchase=[BringPurchase(uuid=UUID('43bdd5a2-740a-4230-8b27-d0bbde886da7'), itemId='Paprika', specification='grün', attributes=[]), BringPurchase(uuid=UUID('2de9d1c0-c211-4129-b6c5-c1260c3fc735'), itemId='Zucchetti', specification='gelb', attributes=[])], recently=[BringPurchase(uuid=UUID('5681ed79-c8e4-4c8b-95ec-112999d016c0'), itemId='Paprika', specification='rot', attributes=[]), BringPurchase(uuid=UUID('01eea2cd-f433-4263-ad08-3d71317c4298'), itemId='Pouletbrüstli', specification='', attributes=[])])) + BringItemsResponse(uuid='00000000-0000-0000-0000-000000000000', status='SHARED', items=Items(purchase=[BringPurchase(uuid='43bdd5a2-740a-4230-8b27-d0bbde886da7', itemId='Paprika', specification='grün', attributes=[]), BringPurchase(uuid='2de9d1c0-c211-4129-b6c5-c1260c3fc735', itemId='Zucchetti', specification='gelb', attributes=[])], recently=[BringPurchase(uuid='5681ed79-c8e4-4c8b-95ec-112999d016c0', itemId='Paprika', specification='rot', attributes=[]), BringPurchase(uuid='01eea2cd-f433-4263-ad08-3d71317c4298', itemId='Pouletbrüstli', specification='', attributes=[])])) # --- # name: TestGetUserAccount.test_get_user_account - BringSyncCurrentUserResponse(email='{email}', emailVerified=True, premiumConfiguration={'hasPremium': False, 'hideSponsoredProducts': False, 'hideSponsoredTemplates': False, 'hideSponsoredPosts': False, 'hideSponsoredCategories': False, 'hideOffersOnMain': False}, publicUserUuid=UUID('00000000-0000-0000-0000-000000000000'), userLocale=UserLocale(language='de', country='DE'), userUuid=UUID('00000000-0000-0000-0000-000000000000'), name='{user_name}', photoPath='bring/user/portrait/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx') + BringSyncCurrentUserResponse(email='{email}', emailVerified=True, premiumConfiguration={'hasPremium': False, 'hideSponsoredProducts': False, 'hideSponsoredTemplates': False, 'hideSponsoredPosts': False, 'hideSponsoredCategories': False, 'hideOffersOnMain': False}, publicUserUuid='00000000-0000-0000-0000-000000000000', userLocale=UserLocale(language='de', country='DE'), userUuid='00000000-0000-0000-0000-000000000000', name='{user_name}', photoPath='bring/user/portrait/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx') # --- # name: TestLoadLists.test_load_lists - BringListResponse(lists=[BringList(listUuid=UUID('00000000-0000-0000-0000-000000000000'), name='Einkauf', theme='ch.publisheria.bring.theme.home')]) + BringListResponse(lists=[BringList(listUuid='00000000-0000-0000-0000-000000000000', name='Einkauf', theme='ch.publisheria.bring.theme.home')]) # --- # name: TestLogin.test_login - BringAuthResponse(uuid=UUID('00000000-0000-0000-0000-000000000000'), publicUuid=UUID('00000000-0000-0000-0000-000000000000'), bringListUUID=UUID('00000000-0000-0000-0000-000000000000'), access_token='ACCESS_TOKEN', refresh_token='REFRESH_TOKEN', token_type='Bearer', expires_in=604799, photoPath='', email='EMAIL', name='NAME') + BringAuthResponse(uuid='00000000-0000-0000-0000-000000000000', publicUuid='00000000-0000-0000-0000-000000000000', bringListUUID='00000000-0000-0000-0000-000000000000', access_token='ACCESS_TOKEN', refresh_token='REFRESH_TOKEN', token_type='Bearer', expires_in=604799, photoPath='', email='EMAIL', name='NAME') # --- # name: TestRetrieveNewAccessToken.test_retrieve_new_access_token BringAuthTokenResponse(access_token='{access_token}', refresh_token='{refresh_token}', token_type='Bearer', expires_in=604799) diff --git a/tests/test_bring.py b/tests/test_bring.py index f4762e2..0473173 100644 --- a/tests/test_bring.py +++ b/tests/test_bring.py @@ -4,7 +4,6 @@ import enum from http import HTTPStatus import time -import uuid import aiohttp from dotenv import load_dotenv @@ -225,8 +224,8 @@ async def mocked__load_article_translations(*args, **kwargs): assert data == snapshot assert bring.headers["Authorization"] == "Bearer ACCESS_TOKEN" assert bring.headers["X-BRING-COUNTRY"] == "DE" - assert bring.uuid == uuid.UUID(UUID) - assert bring.public_uuid == uuid.UUID(UUID) + assert bring.uuid == UUID + assert bring.public_uuid == UUID assert bring.user_locale == "de-DE" @@ -1561,7 +1560,7 @@ async def test_get_activity( ) monkeypatch.setattr(bring, "uuid", UUID) - activity = await bring.get_activity(uuid.UUID(UUID)) + activity = await bring.get_activity(UUID) assert activity == snapshot @@ -1581,7 +1580,7 @@ async def test_request_exception(self, mocked, bring, exception): ) with pytest.raises(BringRequestException): - await bring.get_activity(uuid.UUID(UUID)) + await bring.get_activity(UUID) async def test_auth_exception(self, mocked, bring): """Test request exceptions.""" @@ -1593,7 +1592,7 @@ async def test_auth_exception(self, mocked, bring): ) with pytest.raises(BringAuthException): - await bring.get_activity(uuid.UUID(UUID)) + await bring.get_activity(UUID) @pytest.mark.parametrize( ("status", "exception"), @@ -1613,4 +1612,4 @@ async def test_parse_exception(self, mocked, bring, status, exception): ) with pytest.raises(exception): - await bring.get_activity(uuid.UUID(UUID)) + await bring.get_activity(UUID)