2024-09-26 14:43:30 +00:00
|
|
|
import requests
|
|
|
|
import dataclasses
|
|
|
|
from typing import Any
|
|
|
|
import functools
|
2024-09-26 16:49:28 +00:00
|
|
|
from collections.abc import Iterator
|
|
|
|
|
|
|
|
PREFIX = 'PAR-' # TODO: Make configurable
|
2024-09-26 14:43:30 +00:00
|
|
|
|
|
|
|
# Types
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class SeqId:
|
|
|
|
raw_id: int
|
|
|
|
|
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class CardId:
|
|
|
|
raw_id: str
|
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
@dataclasses.dataclass(frozen=True)
|
|
|
|
class Card:
|
|
|
|
card_id: CardId
|
|
|
|
seq_id: SeqId
|
|
|
|
seq_id_with_prefix: str
|
|
|
|
detailed_description: str
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def from_json(json: dict[str, Any]) -> 'Card':
|
|
|
|
print(json)
|
|
|
|
return Card(
|
|
|
|
card_id = CardId(json['cardId']),
|
|
|
|
seq_id = SeqId(json['cardSequentialId']),
|
|
|
|
seq_id_with_prefix = PREFIX + json['cardSequentialId'],
|
|
|
|
detailed_description = json['detailedDescription'],
|
|
|
|
)
|
|
|
|
|
2024-09-26 14:43:30 +00:00
|
|
|
# Endpoints
|
|
|
|
URL_API_ROOT = 'https://favro.com/api/v1'
|
|
|
|
URL_GET_ALL_CARDS = URL_API_ROOT+'/cards'
|
|
|
|
URL_UPDATE_CARD = URL_API_ROOT+'/cards/{card_id}'
|
|
|
|
|
|
|
|
class FavroClient:
|
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
def __init__(self, *, favro_org_id: str, favro_username: str, favro_password: str,
|
2024-09-26 14:43:30 +00:00
|
|
|
session: requests.Session | None = None):
|
|
|
|
# Setup session
|
|
|
|
self.session = session or requests.Session()
|
|
|
|
self.session.auth = (favro_username, favro_username)
|
|
|
|
self.session.headers.update({
|
|
|
|
'organizationId': favro_org_id,
|
|
|
|
'content-type': 'application/json',
|
|
|
|
})
|
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
def get_todo_list_cards(self) -> Iterator[Card]:
|
|
|
|
yield from self.get_cards(todo_list=True)
|
|
|
|
|
2024-09-26 14:43:30 +00:00
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
def get_cards(self, *, seqid: SeqId | None = None, todo_list=True) -> Iterator[Card]:
|
|
|
|
# Determine params for get_cards
|
|
|
|
params = {}
|
|
|
|
if seqid:
|
|
|
|
params['cardSequentialId']= seqid.raw_id
|
|
|
|
if todo_list:
|
|
|
|
params['todoList'] = True
|
2024-09-26 14:43:30 +00:00
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
# Run query
|
2024-09-26 14:43:30 +00:00
|
|
|
response = self.session.get(URL_GET_ALL_CARDS, params = params)
|
|
|
|
response.raise_for_status()
|
|
|
|
json = response.json()
|
2024-09-26 16:49:28 +00:00
|
|
|
|
|
|
|
# TODO: Pageination
|
|
|
|
for entity_json in json['entities']:
|
|
|
|
yield Card.from_json(entity_json)
|
|
|
|
del entity_json
|
|
|
|
|
|
|
|
def get_card(self, seqid: SeqId) -> Card:
|
|
|
|
return next(self.get_cards(seqid=seqid))
|
2024-09-26 14:43:30 +00:00
|
|
|
|
|
|
|
@functools.cache
|
2024-09-26 16:49:28 +00:00
|
|
|
def get_card_id(self, seqid: SeqId) -> CardId:
|
|
|
|
json = self.get_card_json(seqid)
|
2024-09-26 14:43:30 +00:00
|
|
|
return CardId(json['cardId'])
|
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
def update_card_description(self, card_id: CardId, description: str) -> Card:
|
|
|
|
"""Returns updated Card."""
|
|
|
|
json_body = {
|
|
|
|
'detailedDescription': description,
|
|
|
|
'descriptionFormat': 'markdown',
|
|
|
|
}
|
|
|
|
|
|
|
|
response = self.session.put(URL_UPDATE_CARD.format(card_id=card_id.raw_id), json=json_body)
|
|
|
|
response.raise_for_status()
|
2024-09-26 14:43:30 +00:00
|
|
|
|
2024-09-26 16:49:28 +00:00
|
|
|
return Card.from_json(response.json())
|