[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 (
|
from .favro_data_model import (
|
||||||
Card,
|
Card,
|
||||||
CardId,
|
CardId,
|
||||||
|
Collection,
|
||||||
CustomFieldId,
|
CustomFieldId,
|
||||||
CustomFieldInfo,
|
CustomFieldInfo,
|
||||||
OrganizationId,
|
OrganizationId,
|
||||||
|
@ -27,12 +28,13 @@ logger = getLogger(__name__)
|
||||||
|
|
||||||
# Endpoints
|
# Endpoints
|
||||||
URL_API_ROOT = 'https://favro.com/api/v1'
|
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_UPDATE_CARD = URL_API_ROOT + '/cards/{card_id}'
|
||||||
URL_GET_USER = URL_API_ROOT + '/users/{user_id}'
|
URL_GET_USER = URL_API_ROOT + '/users/{user_id}'
|
||||||
URL_GET_TAG = URL_API_ROOT + '/tags/{tag_id}'
|
URL_GET_TAG = URL_API_ROOT + '/tags/{tag_id}'
|
||||||
URL_GET_CUSTOM_FIELD = URL_API_ROOT + '/customfields/{custom_field_id}'
|
URL_GET_CUSTOM_FIELD = URL_API_ROOT + '/customfields/{custom_field_id}'
|
||||||
URL_GET_TASKS = URL_API_ROOT + '/tasks'
|
URL_GET_TASKS = URL_API_ROOT + '/tasks'
|
||||||
|
URL_GET_COLLECTIONS = URL_API_ROOT + '/collections'
|
||||||
|
|
||||||
|
|
||||||
class CardCache:
|
class CardCache:
|
||||||
|
@ -105,7 +107,9 @@ class FavroClient:
|
||||||
next(self.get_todo_list_cards())
|
next(self.get_todo_list_cards())
|
||||||
|
|
||||||
def get_todo_list_cards(self) -> Iterator[Card]:
|
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(
|
def get_cards(
|
||||||
self,
|
self,
|
||||||
|
@ -113,35 +117,18 @@ class FavroClient:
|
||||||
seq_id: SeqId | None = None,
|
seq_id: SeqId | None = None,
|
||||||
todo_list=False,
|
todo_list=False,
|
||||||
) -> Iterator[Card]:
|
) -> Iterator[Card]:
|
||||||
page = 0
|
request = self._get_cards_request(seq_id=seq_id,todo_list=todo_list)
|
||||||
request_id = None
|
yield from self._get_paginated(request, Card.from_json)
|
||||||
num_pages = 1
|
|
||||||
|
|
||||||
while page < num_pages:
|
def get_collections(self) -> Iterator[Collection]:
|
||||||
# Determine params for get_cards
|
request = requests.Request('GET', URL_GET_COLLECTIONS)
|
||||||
request = self._get_cards_request(
|
yield from self._get_paginated(request, Collection.from_json)
|
||||||
seq_id,
|
|
||||||
todo_list,
|
|
||||||
page=page,
|
|
||||||
request_id=request_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run query
|
def _get_cards_prepared_request(
|
||||||
logger.warning('Sending request: %s', request.url)
|
self, **kwargs,
|
||||||
response = self.session.send(request)
|
) -> requests.PreparedRequest:
|
||||||
response.raise_for_status()
|
request = self._get_cards_request(**kwargs)
|
||||||
json = response.json()
|
return self.session.prepare_request(request)
|
||||||
|
|
||||||
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_request(
|
def _get_cards_request(
|
||||||
self,
|
self,
|
||||||
|
@ -149,7 +136,7 @@ class FavroClient:
|
||||||
todo_list: bool = False,
|
todo_list: bool = False,
|
||||||
request_id: None | str = None,
|
request_id: None | str = None,
|
||||||
page: None | int = None,
|
page: None | int = None,
|
||||||
) -> requests.PreparedRequest:
|
) -> requests.Request:
|
||||||
params = {'descriptionFormat': 'markdown'}
|
params = {'descriptionFormat': 'markdown'}
|
||||||
if seq_id is not None:
|
if seq_id is not None:
|
||||||
params['cardSequentialId'] = str(seq_id.raw_id)
|
params['cardSequentialId'] = str(seq_id.raw_id)
|
||||||
|
@ -160,8 +147,7 @@ class FavroClient:
|
||||||
if page:
|
if page:
|
||||||
params['page'] = page
|
params['page'] = page
|
||||||
|
|
||||||
request = requests.Request('GET', URL_GET_ALL_CARDS, params=params)
|
return requests.Request('GET', URL_GET_CARDS, params=params)
|
||||||
return self.session.prepare_request(request)
|
|
||||||
|
|
||||||
def get_card(self, seq_id: SeqId) -> Card:
|
def get_card(self, seq_id: SeqId) -> Card:
|
||||||
if card := self.cache.get_card_by_seq_id(seq_id):
|
if card := self.cache.get_card_by_seq_id(seq_id):
|
||||||
|
@ -190,7 +176,7 @@ class FavroClient:
|
||||||
card = self.cache.remove(card_id)
|
card = self.cache.remove(card_id)
|
||||||
if card:
|
if card:
|
||||||
self.session.cache.delete(
|
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(
|
def update_card_contents_locally(
|
||||||
|
@ -241,3 +227,30 @@ class FavroClient:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
self._invalidate_cache(card_id)
|
self._invalidate_cache(card_id)
|
||||||
return self.update_card_contents_locally(card_id, card_contents)
|
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
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class CollectionId:
|
||||||
|
raw_id: int
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(frozen=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class SeqId:
|
class SeqId:
|
||||||
raw_id: int
|
raw_id: int
|
||||||
|
@ -238,6 +243,29 @@ class Task:
|
||||||
position=json['position'],
|
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)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Card:
|
class Card:
|
||||||
|
|
|
@ -50,6 +50,17 @@ def test_get_cards():
|
||||||
for card in client.get_cards(todo_list=True):
|
for card in client.get_cards(todo_list=True):
|
||||||
assert_valid_card(card)
|
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():
|
def create_client():
|
||||||
session = requests_cache.CachedSession('output/test-http-cache.sqlite')
|
session = requests_cache.CachedSession('output/test-http-cache.sqlite')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user