Skip to content

Commit

Permalink
feat: providing some actions with issues, queues and users from API (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
danfimov committed Sep 21, 2023
2 parents 7bbca90 + 07496d3 commit 23c9a03
Show file tree
Hide file tree
Showing 33 changed files with 1,312 additions and 118 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/code-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ jobs:
python-version: '3.11'
- run: pip install --upgrade pip
- run: make install
- run: make test-cov
- run: make test
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: check-added-large-files

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.287'
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ test: ##@Testing Test application with pytest
$(TEST)

test-cov: ##@Testing Test application with pytest and create coverage report
$(TEST) --cov=$(APPLICATION_NAME) --cov-report html --cov-fail-under=85
$(TEST) --cov=$(APPLICATION_NAME) --cov-report html --cov-fail-under=85 --cov-config pyproject.toml

lint: ##@Code Check code with pylint
lint: ##@Code Check code with ruff
poetry run python3 -m ruff $(CODE) tests

format: ##@Code Reformat code with ruff and black
format: ##@Code Reformat code with ruff
poetry run python3 -m ruff $(CODE) tests --fix

precommit: # Code Check code with pre-commit hooks
pre-commit run --all-files

clean: ##@Code Clean directory from garbage files
rm -fr *.egg-info dist
46 changes: 44 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,44 @@
# ya_tracker_client
Async Yandex Tracker Client
# Yandex Tracker Client (or Yet Another Tracker Client)

Async Yandex Tracker Client based on aiohttp and pydantic (v2)

# Current library capabilities

- Working with queues
- Getting information about issues, priorities and transitions
- Working with issue relationships
- Getting user information

# TBD

## Issues

- [ ] Перенести задачу в другую очередь
- [ ] Узнать количество задач
- [ ] Найти задачи
- [ ] Освободить ресурсы просмотра прокрутки
- [ ] Выполнить переход в статус
- [ ] Получить историю изменений задачи

## Queues - automation

- [ ] Создать автодействие
- [ ] Получить параметры автодействия
- [ ] Создать триггер
- [ ] Получить параметры триггера

# TBD in a long run

- Checklists
- Projects
- Comments
- Macros
- Internal links
- Attachments
- Tables of tasks
- Components
- Import
- Package operations
- Time scoring
- Issues fields
- Users
18 changes: 13 additions & 5 deletions examples/get_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,28 @@

load_dotenv()
# from registered application at Yandex OAuth - https://oauth.yandex.ru/
API_TOKEN = os.getenv('API_TOKEN')
API_TOKEN = os.getenv("API_TOKEN")
# from admin panel at Yandex Tracker - https://tracker.yandex.ru/admin/orgs
API_ORGANISATION_ID = os.getenv('API_ORGANISATION_ID')
API_ORGANISATION_ID = os.getenv("API_ORGANISATION_ID")


async def main() -> None:
client = YaTrackerClient(
organisation_id=API_ORGANISATION_ID,
oauth_token=API_TOKEN,
)
issue = await client.get_issue('TRACKER-1')
print(issue)

me = await client.get_myself()
print(me)

me = await client.get_user(me.login, me.uid)
print(me)

all_me = await client.get_users()
print(all_me)

await client.stop()


if __name__ == '__main__':
if __name__ == "__main__":
run(main())
630 changes: 554 additions & 76 deletions poetry.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ coverage = "^7.3.1"
pytest-asyncio = "^0.21.1"
pytest-cov = "^4.1.0"
ruff = "^0.0.287"
greenlet = "^2.0.2"
polyfactory = "^2.8.2"
gevent = "^23.9.1"
pre-commit = "^3.4.0"

[tool.poetry.group.examples.dependencies]
python-dotenv = "^1.0.0"
Expand Down Expand Up @@ -63,7 +64,7 @@ directory = "pytest-cov-report"

[tool.coverage.run]
branch = true
concurrency = ['thread', 'greenlet']
concurrency = ['gevent']

[tool.pytest.ini_options]
asyncio_mode = "auto"
Expand All @@ -83,4 +84,4 @@ max-complexity = 10

[tool.ruff.isort]
known-local-folder = ["ya_tacker_client"]
lines-after-imports = 2
lines-after-imports = 2
13 changes: 11 additions & 2 deletions ya_tracker_client/domain/client/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from abc import ABC, abstractmethod
from http import HTTPStatus
from json import dumps
from logging import getLogger
from typing import Any

from aiohttp import BytesPayload

from ya_tracker_client.domain.client.errors import (
ClientAuthError,
ClientError,
Expand Down Expand Up @@ -55,11 +58,17 @@ async def request(
payload: dict[str, Any] | None = None,
) -> bytes:
uri = f"{self._base_url}/{self._api_version}{uri}"

bytes_payload = BytesPayload(
value=bytes(dumps(payload), encoding="utf-8"),
content_type="application/json",
)

status, body = await self._make_request(
method=method,
url=uri,
params=params,
data=payload,
data=bytes_payload,
)
self._check_status(status, body)
return body
Expand All @@ -70,7 +79,7 @@ async def _make_request(
method: str,
url: str,
params: dict[str, Any] | None = None,
data: bytes | None = None,
data: bytes | BytesPayload | None = None,
) -> tuple[int, bytes]:
"""
Get raw response from via http-client.
Expand Down
51 changes: 36 additions & 15 deletions ya_tracker_client/domain/entities/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from pydantic import AliasChoices, Field

from ya_tracker_client.domain.entities.base import AbstractEntity
from ya_tracker_client.domain.entities.issue_type import IssueType
from ya_tracker_client.domain.entities.priority import Priority
from ya_tracker_client.domain.entities.queue import Queue
from ya_tracker_client.domain.entities.queue import QueueIdentifier, QueueShort
from ya_tracker_client.domain.entities.sprint import Sprint
from ya_tracker_client.domain.entities.status import Status
from ya_tracker_client.domain.entities.user import User
from ya_tracker_client.domain.entities.user import UserShort


class IssueShort(AbstractEntity):
Expand All @@ -17,13 +18,6 @@ class IssueShort(AbstractEntity):
display: str


class IssueType(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: str
key: str
display: str


class Issue(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: str
Expand All @@ -36,19 +30,46 @@ class Issue(AbstractEntity):
sprint: list[Sprint] | None = None
type: IssueType
priority: Priority
followers: list[User] | None = None
queue: Queue
previous_queue: Queue | None = None
followers: list[UserShort] | None = None
queue: QueueShort
previous_queue: QueueShort | None = None
favorite: bool
assignee: User | None = None
assignee: UserShort | None = None

last_comment_update_at: datetime | None = None
aliases: list[str] | None = None
updated_by: User | None = None
updated_by: UserShort | None = None
created_at: datetime = Field(validation_alias=AliasChoices("createdAt", "created_at"))
created_by: User = Field(validation_alias=AliasChoices("createdBy", "created_by"))
created_by: UserShort = Field(validation_alias=AliasChoices("createdBy", "created_by"))
votes: int
updated_at: datetime | None = None
status: Status
previous_status: Status | None = None
direction: str | None = None


class IssueCreate(AbstractEntity):
summary: str
queue: QueueIdentifier | str | int
parent: IssueShort | str | None = None
description: str | None = None
sprint: list[Sprint | str] | None = None
type: IssueType | None = None
priority: Priority | None = None
followers: list[UserShort | str] | None = None
assignee: list[UserShort | str] | None = None
unique: str | None = None
attachment_ids: list[str] | None = Field(
default=None,
validation_alias=AliasChoices("attachmentIds", "attachment_ids"),
)


class IssueEdit(AbstractEntity):
summary: str | None = None
parent: IssueShort | str | None = None
description: str | None = None
sprint: list[Sprint | str] | None = None
type: IssueType | None = None
priority: Priority | None = None
followers: list[UserShort | str] | None = None
47 changes: 47 additions & 0 deletions ya_tracker_client/domain/entities/issue_relationship.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from datetime import datetime
from enum import Enum

from pydantic import AliasChoices, Field

from ya_tracker_client.domain.entities.base import AbstractEntity
from ya_tracker_client.domain.entities.issue import IssueShort
from ya_tracker_client.domain.entities.issue_status import IssueStatus
from ya_tracker_client.domain.entities.user import UserShort


class IssueRelationshipTypeEnum(str, Enum):
RELATES = "relates"
IS_DEPENDENT_BY = "is dependent by"
DEPENDS_ON = "depends on"
IS_SUBTASK_FOR = "is subtask for"
IS_PARENT_TASK_FOR = "is parent task for"
DUPLICATES = "duplicates"
IS_DUPLICATED_BY = "is duplicated by"
IS_EPIC_OF = "is epic of"
HAS_EPIC = "has epic"


class IssueRelationshipType(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: str
inward: str
outward: str


class IssueRelationship(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: int
type: IssueRelationshipType
direction: str
object: IssueShort
created_at: datetime = Field(validation_alias=AliasChoices("createdAt", "created_at"))
updated_at: datetime = Field(validation_alias=AliasChoices("updatedAt", "updated_at"))
created_by: UserShort = Field(validation_alias=AliasChoices("createdBy", "created_by"))
updated_by: UserShort = Field(validation_alias=AliasChoices("updatedBy", "updated_by"))
assignee: UserShort | None = None
status: IssueStatus


class IssueRelationshipCreate(AbstractEntity):
issue: str
relationship: IssueRelationshipTypeEnum
10 changes: 10 additions & 0 deletions ya_tracker_client/domain/entities/issue_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pydantic import AliasChoices, Field

from ya_tracker_client.domain.entities.base import AbstractEntity


class IssueStatus(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: str
key: str
display: str
10 changes: 10 additions & 0 deletions ya_tracker_client/domain/entities/issue_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pydantic import AliasChoices, Field

from ya_tracker_client.domain.entities.base import AbstractEntity


class IssueType(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: str
key: str
display: str
12 changes: 12 additions & 0 deletions ya_tracker_client/domain/entities/issue_type_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pydantic import AliasChoices, Field

from ya_tracker_client.domain.entities.base import AbstractEntity
from ya_tracker_client.domain.entities.issue_type import IssueType
from ya_tracker_client.domain.entities.resolution import ResolutionShort
from ya_tracker_client.domain.entities.workflow import WorkflowShort


class IssueTypeConfig(AbstractEntity):
issue_type: IssueType = Field(validation_alias=AliasChoices("issueType", "issue_type"))
workflow: WorkflowShort
resolutions: list[ResolutionShort]
2 changes: 1 addition & 1 deletion ya_tracker_client/domain/entities/priority.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class Priority(AbstractEntity):
url: str = Field(validation_alias=AliasChoices("self", "url"))
id: str
id: int
key: str
display: str | None = None
version: int | None = None
Expand Down
Loading

0 comments on commit 23c9a03

Please sign in to comment.