[Client]: Support collections
All checks were successful
Test Python / Test (push) Successful in 26s
All checks were successful
Test Python / Test (push) Successful in 26s
This commit is contained in:
parent
d4a56d8d91
commit
471704d9fe
|
@ -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']
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue
Block a user