-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
1,952 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: Checks | ||
on: [pull_request] | ||
|
||
jobs: | ||
lint: | ||
runs-on: ubuntu-latest | ||
name: Lint code | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.11' | ||
- run: pip install --upgrade pip | ||
- run: make install | ||
- run: make lint | ||
test: | ||
runs-on: ubuntu-latest | ||
name: Run tests | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.11' | ||
- run: pip install --upgrade pip | ||
- run: make install | ||
- run: make test-cov |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
args := $(wordlist 2, 100, $(MAKECMDGOALS)) | ||
|
||
APPLICATION_NAME = ya_tracker_client | ||
|
||
HELP_FUN = \ | ||
%help; while(<>){push@{$$help{$$2//'options'}},[$$1,$$3] \ | ||
if/^([\w-_]+)\s*:.*\#\#(?:@(\w+))?\s(.*)$$/}; \ | ||
print"$$_:\n", map" $$_->[0]".(" "x(20-length($$_->[0])))."$$_->[1]\n",\ | ||
@{$$help{$$_}},"\n" for keys %help; \ | ||
CODE = ya_tracker_client | ||
TEST = poetry run python3 -m pytest --verbosity=2 --showlocals --log-level=DEBUG | ||
|
||
ifndef args | ||
MESSAGE = "No such command (or you pass two or many targets to ). List of possible commands: make help" | ||
else | ||
MESSAGE = "Done" | ||
endif | ||
|
||
|
||
help: ##@Help Show this help | ||
@echo -e "Usage: make [target] ...\n" | ||
@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST) | ||
|
||
install: ##@Setup Install project requirements | ||
python3 -m pip install poetry | ||
poetry install | ||
|
||
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 | ||
|
||
lint: ##@Code Check code with pylint | ||
poetry run python3 -m ruff $(CODE) tests | ||
|
||
format: ##@Code Reformat code with ruff and black | ||
poetry run python3 -m ruff $(CODE) tests --fix | ||
|
||
clean: ##@Code Clean directory from garbage files | ||
rm -fr *.egg-info dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import os | ||
from asyncio import run | ||
|
||
from dotenv import load_dotenv | ||
|
||
from ya_tracker_client import YaTrackerClient | ||
|
||
|
||
load_dotenv() | ||
# from registered application at Yandex OAuth - https://oauth.yandex.ru/ | ||
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') | ||
|
||
|
||
async def main() -> None: | ||
client = YaTrackerClient( | ||
organisation_id=API_ORGANISATION_ID, | ||
oauth_token=API_TOKEN, | ||
) | ||
issue = await client.get_issue('TRACKER-1') | ||
print(issue) | ||
await client.stop() | ||
|
||
|
||
if __name__ == '__main__': | ||
run(main()) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
[tool.poetry] | ||
name = "ya_tracker_client" | ||
version = "0.1.0" | ||
description = "Async Yandex Tracker Client" | ||
authors = ["Дмитрий Анфимов <[email protected]>"] | ||
license = "MIT" | ||
readme = "README.md" | ||
packages = [{include = "ya_tracker_client"}] | ||
|
||
[[tool.poetry.source]] | ||
name = "default-pypi" | ||
url = "https://pypi.org/simple" | ||
priority = "default" | ||
|
||
[[tool.poetry.source]] | ||
name = "yandex-pypi" | ||
url = "https://pypi.yandex-team.ru/simple" | ||
priority = "supplemental" | ||
|
||
[tool.poetry.dependencies] | ||
python = "^3.11" | ||
aiohttp = "^3.8.5" | ||
pydantic = "^2.3.0" | ||
certifi = "^2023.7.22" | ||
|
||
[tool.poetry.group.dev.dependencies] | ||
pytest = "^7.4.2" | ||
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" | ||
|
||
[tool.poetry.group.examples.dependencies] | ||
python-dotenv = "^1.0.0" | ||
|
||
[build-system] | ||
requires = ["poetry-core"] | ||
build-backend = "poetry.core.masonry.api" | ||
|
||
[tool.pytest] | ||
python_files = "test_*.py" | ||
python_classes = ["*Test", "Test*"] | ||
python_functions = "test_*" | ||
testpaths = "tests/" | ||
|
||
[tool.coverage.report] | ||
exclude_lines = [ | ||
"# pragma: no cover", | ||
"def __repr__", | ||
"def __str__", | ||
"def __unicode__", | ||
] | ||
show_missing = true | ||
skip_empty = true | ||
omit = [ | ||
"*/__init__.py", | ||
] | ||
|
||
[tool.coverage.html] | ||
directory = "pytest-cov-report" | ||
|
||
[tool.coverage.run] | ||
branch = true | ||
concurrency = ['thread', 'greenlet'] | ||
|
||
[tool.pytest.ini_options] | ||
asyncio_mode = "auto" | ||
|
||
[tool.ruff] | ||
line-length = 120 | ||
select = ["W", "E", "F", "Q", "B", "I", "N", "ASYNC", "G", "RUF", "COM", "C90"] | ||
ignore = [] | ||
target-version = "py311" | ||
|
||
[tool.ruff.flake8-quotes] | ||
docstring-quotes = "double" | ||
|
||
[tool.ruff.mccabe] | ||
# Unlike Flake8, default to a complexity level of 10. | ||
max-complexity = 10 | ||
|
||
[tool.ruff.isort] | ||
known-local-folder = ["ya_tacker_client"] | ||
lines-after-imports = 2 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from polyfactory.factories.pydantic_factory import ModelFactory | ||
from polyfactory.pytest_plugin import register_fixture | ||
|
||
from ya_tracker_client.domain.entities.issue import Issue | ||
|
||
|
||
@register_fixture | ||
class IssueFactory(ModelFactory[Issue]): | ||
__model__ = Issue |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
from http import HTTPStatus | ||
from random import randint | ||
from typing import Any | ||
|
||
import pytest | ||
|
||
from ya_tracker_client.domain.client import BaseClient | ||
from ya_tracker_client.domain.client.errors import ClientInitTokenError | ||
|
||
|
||
class ClientForTestInitialization(BaseClient): | ||
auth_token_header_value = "" | ||
organisation_token_name = "" | ||
|
||
async def _make_request( | ||
self, | ||
method: str, | ||
url: str, | ||
params: dict[str, Any] | None = None, | ||
data: bytes | None = None, | ||
) -> tuple[int, bytes]: | ||
return HTTPStatus.OK, b"Test response body" | ||
|
||
async def stop(self) -> None: | ||
pass | ||
|
||
def test_authorization_header(self) -> None: | ||
assert self._headers.get("Authorization") == self.auth_token_header_value | ||
|
||
def test_organisation_header(self) -> None: | ||
assert self._headers.get(self.organisation_token_name) | ||
|
||
|
||
def get_client_for_test_initialization( | ||
organisation_id: str | int, | ||
oauth_token: str | None = None, | ||
iam_token: str | None = None, | ||
auth_token_header_value: str = "", | ||
organisation_token_name: str = "", | ||
) -> ClientForTestInitialization: | ||
client = ClientForTestInitialization( | ||
organisation_id=organisation_id, | ||
oauth_token=oauth_token, | ||
iam_token=iam_token, | ||
) | ||
client.auth_token_header_value = auth_token_header_value | ||
client.organisation_token_name = organisation_token_name | ||
return client | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"organisation_id, header_name", | ||
( | ||
(randint(1, 1000), "X-Org-Id"), | ||
(str(randint(1, 1000)), "X-Org-Id"), | ||
("test_organisation_id", "X-Cloud-Org-Id"), | ||
), | ||
) | ||
def test_init__when_organisation_id_passed__then_use_specific_header_name(organisation_id, header_name) -> None: | ||
client = get_client_for_test_initialization( | ||
organisation_id=organisation_id, | ||
oauth_token="some_token", | ||
auth_token_header_value="OAuth some_token", | ||
organisation_token_name=header_name, | ||
) | ||
client.test_organisation_header() | ||
|
||
|
||
def test_init__when_auth_token_not_passed__then_raise_error() -> None: | ||
with pytest.raises(ClientInitTokenError): | ||
get_client_for_test_initialization( | ||
organisation_id=randint(1, 1000), | ||
) | ||
|
||
|
||
def test_init__when_oauth_token_passed__then_construct_specific_header_value() -> None: | ||
client = get_client_for_test_initialization( | ||
organisation_id=randint(1, 1000), | ||
oauth_token="some_test_token", | ||
auth_token_header_value="OAuth some_test_token", | ||
) | ||
client.test_authorization_header() | ||
|
||
|
||
def test_init__when_iam_token_passed__then_construct_specific_header_value() -> None: | ||
client = get_client_for_test_initialization( | ||
organisation_id=randint(1, 1000), | ||
iam_token="some_test_token", | ||
auth_token_header_value="Bearer some_test_token", | ||
) | ||
client.test_authorization_header() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from http import HTTPStatus | ||
from logging import getLogger | ||
from random import randint | ||
from typing import TYPE_CHECKING, Any, Type | ||
|
||
import pytest | ||
|
||
from ya_tracker_client.domain.client import BaseClient | ||
from ya_tracker_client.domain.client.errors import ( | ||
ClientAuthError, | ||
ClientError, | ||
ClientObjectConflictError, | ||
ClientObjectNotFoundError, | ||
ClientSufficientRightsError, | ||
) | ||
|
||
|
||
if TYPE_CHECKING: | ||
pass | ||
|
||
|
||
logger = getLogger(__name__) | ||
|
||
|
||
class ClientForTestRequestStatus(BaseClient): | ||
make_request_status_code = 200 | ||
|
||
async def _make_request( | ||
self, | ||
method: str, | ||
url: str, | ||
params: dict[str, Any] | None = None, | ||
data: bytes | None = None, | ||
) -> tuple[int, bytes]: | ||
return self.make_request_status_code, b"Test response body" | ||
|
||
async def stop(self) -> None: | ||
pass | ||
|
||
|
||
def create_client_for_test_request_status(status_code: int) -> ClientForTestRequestStatus: | ||
client = ClientForTestRequestStatus( | ||
organisation_id=randint(1, 1000), | ||
oauth_token="test_token", | ||
) | ||
client.make_request_status_code = status_code | ||
return client | ||
|
||
|
||
class TestCheckStatus: | ||
""" | ||
Test with all statuses from documentation: https://cloud.yandex.com/en/docs/tracker/error-codes | ||
""" | ||
|
||
@pytest.mark.parametrize( | ||
"status_code", | ||
( | ||
HTTPStatus.OK, | ||
HTTPStatus.CREATED, | ||
HTTPStatus.NO_CONTENT, | ||
), | ||
) | ||
async def test_request__when_status_ok__then_not_raise_error(self, status_code: int) -> None: | ||
client = create_client_for_test_request_status(status_code) | ||
response_body = await client.request("GET", "/test_uri") | ||
assert response_body == b"Test response body" | ||
|
||
@pytest.mark.parametrize( | ||
"status_code, error_type", | ||
( | ||
(HTTPStatus.BAD_REQUEST, ClientError), | ||
(HTTPStatus.UNAUTHORIZED, ClientAuthError), | ||
(HTTPStatus.FORBIDDEN, ClientSufficientRightsError), | ||
(HTTPStatus.NOT_FOUND, ClientObjectNotFoundError), | ||
(HTTPStatus.CONFLICT, ClientObjectConflictError), | ||
(HTTPStatus.PRECONDITION_FAILED, ClientObjectConflictError), | ||
(HTTPStatus.UNPROCESSABLE_ENTITY, ClientError), | ||
(HTTPStatus.PRECONDITION_REQUIRED, ClientError), | ||
), | ||
) | ||
async def test_request__when_status_not_ok__then_raise_specific_error( | ||
self, | ||
status_code: int, | ||
error_type: Type[ClientError], | ||
) -> None: | ||
client = create_client_for_test_request_status(status_code) | ||
if error_type == ClientError: # always raise ClientException with context | ||
with pytest.raises(error_type, match="Test response body"): | ||
await client.request("GET", "/test_uri") | ||
else: | ||
with pytest.raises(error_type): | ||
await client.request("GET", "/test_uri") |
Oops, something went wrong.