1
0

[Client]: Support collections
All checks were successful
Test Python / Test (push) Successful in 26s

This commit is contained in:
Jon Michael Aanes 2024-10-02 16:23:15 +02:00
parent d4a56d8d91
commit 471704d9fe
3 changed files with 85 additions and 33 deletions

View File

@ -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']

View File

@ -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:

View File

@ -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')