diff --git a/favro_sync/favro_client.py b/favro_sync/favro_client.py index 404b161..9da0946 100644 --- a/favro_sync/favro_client.py +++ b/favro_sync/favro_client.py @@ -12,6 +12,7 @@ import requests from .favro_data_model import ( Card, CardId, + Collection, CustomFieldId, CustomFieldInfo, OrganizationId, @@ -27,12 +28,13 @@ logger = getLogger(__name__) # Endpoints URL_API_ROOT = 'https://favro.com/api/v1' -URL_GET_ALL_CARDS = URL_API_ROOT + '/cards' +URL_GET_CARDS = URL_API_ROOT + '/cards' URL_UPDATE_CARD = URL_API_ROOT + '/cards/{card_id}' URL_GET_USER = URL_API_ROOT + '/users/{user_id}' URL_GET_TAG = URL_API_ROOT + '/tags/{tag_id}' URL_GET_CUSTOM_FIELD = URL_API_ROOT + '/customfields/{custom_field_id}' URL_GET_TASKS = URL_API_ROOT + '/tasks' +URL_GET_COLLECTIONS = URL_API_ROOT + '/collections' class CardCache: @@ -105,7 +107,9 @@ class FavroClient: next(self.get_todo_list_cards()) def get_todo_list_cards(self) -> Iterator[Card]: - yield from self.get_cards(todo_list=True) + for card in self.get_cards(todo_list=True): + self.cache.add_card(card) + yield card def get_cards( self, @@ -113,35 +117,18 @@ class FavroClient: seq_id: SeqId | None = None, todo_list=False, ) -> Iterator[Card]: - page = 0 - request_id = None - num_pages = 1 + request = self._get_cards_request(seq_id=seq_id,todo_list=todo_list) + yield from self._get_paginated(request, Card.from_json) - while page < num_pages: - # Determine params for get_cards - request = self._get_cards_request( - seq_id, - todo_list, - page=page, - request_id=request_id, - ) + def get_collections(self) -> Iterator[Collection]: + request = requests.Request('GET', URL_GET_COLLECTIONS) + yield from self._get_paginated(request, Collection.from_json) - # Run query - logger.warning('Sending request: %s', request.url) - response = self.session.send(request) - response.raise_for_status() - json = response.json() - - for entity_json in json['entities']: - card = Card.from_json(entity_json) - self.cache.add_card(card) - yield card - del entity_json - - # Pagination bookkeeping - page = json['page'] + 1 - request_id = json['requestId'] - num_pages = json['pages'] + def _get_cards_prepared_request( + self, **kwargs, + ) -> requests.PreparedRequest: + request = self._get_cards_request(**kwargs) + return self.session.prepare_request(request) def _get_cards_request( self, @@ -149,7 +136,7 @@ class FavroClient: todo_list: bool = False, request_id: None | str = None, page: None | int = None, - ) -> requests.PreparedRequest: + ) -> requests.Request: params = {'descriptionFormat': 'markdown'} if seq_id is not None: params['cardSequentialId'] = str(seq_id.raw_id) @@ -160,8 +147,7 @@ class FavroClient: if page: params['page'] = page - request = requests.Request('GET', URL_GET_ALL_CARDS, params=params) - return self.session.prepare_request(request) + return requests.Request('GET', URL_GET_CARDS, params=params) def get_card(self, seq_id: SeqId) -> Card: if card := self.cache.get_card_by_seq_id(seq_id): @@ -190,7 +176,7 @@ class FavroClient: card = self.cache.remove(card_id) if card: self.session.cache.delete( - requests=[self._get_cards_request(seq_id=card.seq_id)], + requests=[self._get_cards_prepared_request(seq_id=card.seq_id)], ) def update_card_contents_locally( @@ -241,3 +227,30 @@ class FavroClient: response.raise_for_status() self._invalidate_cache(card_id) return self.update_card_contents_locally(card_id, card_contents) + + def _get_paginated(self, base_request: requests.Request, entity_from_json) -> Iterator: + page = 0 + request_id = None + num_pages = 1 + + while page < num_pages: + # Determine params for get_cards + base_request.params['page'] = page + base_request.params['request_id'] = request_id + request = self.session.prepare_request(base_request) + + # Run query + logger.warning('Sending request: %s', request.url) + response = self.session.send(request) + response.raise_for_status() + json = response.json() + + for entity_json in json['entities']: + entity = entity_from_json(entity_json) + yield entity + del entity_json, entity + + # Pagination bookkeeping + page = json['page'] + 1 + request_id = json['requestId'] + num_pages = json['pages'] diff --git a/favro_sync/favro_data_model.py b/favro_sync/favro_data_model.py index e74e04d..4346a41 100644 --- a/favro_sync/favro_data_model.py +++ b/favro_sync/favro_data_model.py @@ -15,6 +15,11 @@ def map_opt(mapper, value): return None +@dataclasses.dataclass(frozen=True) +class CollectionId: + raw_id: int + + @dataclasses.dataclass(frozen=True) class SeqId: raw_id: int @@ -238,6 +243,29 @@ class Task: position=json['position'], ) +@dataclasses.dataclass(frozen=True) +class Collection: + collection_id: CollectionId + organization_id: OrganizationId + name: str + shared_to_users: list[dict[str,str]] # TODO + public_sharing: str + background: str + is_archived: bool + widget_common_id: WidgetCommonId + + @staticmethod + def from_json(json: dict[str, Any]) -> 'Collection': + return Collection( + collection_id=CollectionId(json['collectionId']), + organization_id=OrganizationId(json['collectionId']), + name=json['name'], + shared_to_users=json['sharedToUsers'], + public_sharing=json['publicSharing'], + background=json['background'], + is_archived=json['archived'], + widget_common_id=map_opt(WidgetCommonId,json.get('widgetCommonId')), + ) @dataclasses.dataclass(frozen=True) class Card: diff --git a/test/test_client.py b/test/test_client.py index 0f2d034..6cacdd0 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -50,6 +50,17 @@ def test_get_cards(): for card in client.get_cards(todo_list=True): assert_valid_card(card) +@needs_secrets +def test_get_collections(): + client = create_client() + for collection in client.get_collections(): + print(collection) + assert collection.collection_id is not None + assert collection.organization_id is not None + assert collection.name is not None + assert collection.background is not None + assert collection.is_archived is not None + assert collection.widget_common_id is None def create_client(): session = requests_cache.CachedSession('output/test-http-cache.sqlite')