trackerkit 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trackerkit/__init__.py +81 -0
- trackerkit/contracts/__init__.py +35 -0
- trackerkit/contracts/auth.py +91 -0
- trackerkit/contracts/comments.py +14 -0
- trackerkit/contracts/connection.py +14 -0
- trackerkit/contracts/projects.py +43 -0
- trackerkit/contracts/relations.py +30 -0
- trackerkit/contracts/task_tracker_client.py +25 -0
- trackerkit/contracts/tasks.py +28 -0
- trackerkit/contracts/users.py +10 -0
- trackerkit/domain/__init__.py +70 -0
- trackerkit/domain/enums.py +28 -0
- trackerkit/domain/errors.py +37 -0
- trackerkit/domain/models.py +131 -0
- trackerkit/domain/relation_mapping.py +215 -0
- trackerkit/factory/__init__.py +4 -0
- trackerkit/factory/client_factory.py +105 -0
- trackerkit/providers/__init__.py +6 -0
- trackerkit/providers/asana/__init__.py +4 -0
- trackerkit/providers/asana/client.py +189 -0
- trackerkit/providers/asana/errors.py +12 -0
- trackerkit/providers/asana/mappers.py +84 -0
- trackerkit/providers/asana/queries.py +58 -0
- trackerkit/providers/asana/transport.py +193 -0
- trackerkit/providers/base.py +47 -0
- trackerkit/providers/jira/__init__.py +4 -0
- trackerkit/providers/jira/client.py +203 -0
- trackerkit/providers/jira/errors.py +29 -0
- trackerkit/providers/jira/mappers.py +126 -0
- trackerkit/providers/jira/queries.py +39 -0
- trackerkit/providers/jira/relations.py +198 -0
- trackerkit/providers/jira/transport.py +176 -0
- trackerkit/providers/yandex_tracker/__init__.py +4 -0
- trackerkit/providers/yandex_tracker/client.py +283 -0
- trackerkit/providers/yandex_tracker/errors.py +17 -0
- trackerkit/providers/yandex_tracker/mappers.py +95 -0
- trackerkit/providers/yandex_tracker/queries.py +62 -0
- trackerkit/providers/yandex_tracker/relations.py +165 -0
- trackerkit/providers/yandex_tracker/transport.py +127 -0
- trackerkit/tracker_client.py +232 -0
- trackerkit-0.3.0.dist-info/METADATA +300 -0
- trackerkit-0.3.0.dist-info/RECORD +44 -0
- trackerkit-0.3.0.dist-info/WHEEL +4 -0
- trackerkit-0.3.0.dist-info/licenses/LICENSE +21 -0
trackerkit/__init__.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Public package entrypoint for trackerkit."""
|
|
2
|
+
|
|
3
|
+
from .contracts.auth import (
|
|
4
|
+
AsanaAuthConfig,
|
|
5
|
+
JiraAuthConfig,
|
|
6
|
+
ProviderAuthConfig,
|
|
7
|
+
YandexTrackerAuthConfig,
|
|
8
|
+
)
|
|
9
|
+
from .domain import (
|
|
10
|
+
AuthenticationError,
|
|
11
|
+
Comment,
|
|
12
|
+
ConfigurationError,
|
|
13
|
+
ConnectionDiagnostic,
|
|
14
|
+
ConnectionErrorKind,
|
|
15
|
+
CreateCommentInput,
|
|
16
|
+
CreateProjectInput,
|
|
17
|
+
CreateRelationInput,
|
|
18
|
+
CreateTaskInput,
|
|
19
|
+
JiraContainsMode,
|
|
20
|
+
JiraLinkTypeMapping,
|
|
21
|
+
JiraRelationMappingConfig,
|
|
22
|
+
Project,
|
|
23
|
+
Provider,
|
|
24
|
+
ProviderCapabilityError,
|
|
25
|
+
ProviderError,
|
|
26
|
+
Relation,
|
|
27
|
+
RelationMappingConfig,
|
|
28
|
+
RelationType,
|
|
29
|
+
Status,
|
|
30
|
+
Task,
|
|
31
|
+
TaskQuery,
|
|
32
|
+
TrackerClientError,
|
|
33
|
+
TrackerKitError,
|
|
34
|
+
UpdateProjectInput,
|
|
35
|
+
UpdateRelationInput,
|
|
36
|
+
UpdateTaskInput,
|
|
37
|
+
User,
|
|
38
|
+
Workspace,
|
|
39
|
+
YandexTrackerRelationMapping,
|
|
40
|
+
YandexTrackerRelationMappingConfig,
|
|
41
|
+
)
|
|
42
|
+
from .tracker_client import TrackerClient
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"AsanaAuthConfig",
|
|
46
|
+
"AuthenticationError",
|
|
47
|
+
"Comment",
|
|
48
|
+
"ConfigurationError",
|
|
49
|
+
"ConnectionDiagnostic",
|
|
50
|
+
"ConnectionErrorKind",
|
|
51
|
+
"CreateCommentInput",
|
|
52
|
+
"CreateProjectInput",
|
|
53
|
+
"CreateRelationInput",
|
|
54
|
+
"CreateTaskInput",
|
|
55
|
+
"JiraAuthConfig",
|
|
56
|
+
"JiraContainsMode",
|
|
57
|
+
"JiraLinkTypeMapping",
|
|
58
|
+
"JiraRelationMappingConfig",
|
|
59
|
+
"Project",
|
|
60
|
+
"Provider",
|
|
61
|
+
"ProviderAuthConfig",
|
|
62
|
+
"ProviderCapabilityError",
|
|
63
|
+
"ProviderError",
|
|
64
|
+
"Relation",
|
|
65
|
+
"RelationMappingConfig",
|
|
66
|
+
"RelationType",
|
|
67
|
+
"Status",
|
|
68
|
+
"Task",
|
|
69
|
+
"TaskQuery",
|
|
70
|
+
"TrackerClient",
|
|
71
|
+
"TrackerClientError",
|
|
72
|
+
"TrackerKitError",
|
|
73
|
+
"UpdateProjectInput",
|
|
74
|
+
"UpdateRelationInput",
|
|
75
|
+
"UpdateTaskInput",
|
|
76
|
+
"User",
|
|
77
|
+
"Workspace",
|
|
78
|
+
"YandexTrackerAuthConfig",
|
|
79
|
+
"YandexTrackerRelationMapping",
|
|
80
|
+
"YandexTrackerRelationMappingConfig",
|
|
81
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from trackerkit.contracts.auth import (
|
|
2
|
+
AsanaAuthConfig,
|
|
3
|
+
JiraAuthConfig,
|
|
4
|
+
ProviderAuthConfig,
|
|
5
|
+
YandexTrackerAuthConfig,
|
|
6
|
+
)
|
|
7
|
+
from trackerkit.contracts.comments import CommentCapability
|
|
8
|
+
from trackerkit.contracts.connection import ConnectionCapability
|
|
9
|
+
from trackerkit.contracts.projects import (
|
|
10
|
+
ProjectReadCapability,
|
|
11
|
+
ProjectWriteCapability,
|
|
12
|
+
WorkspaceCapability,
|
|
13
|
+
)
|
|
14
|
+
from trackerkit.contracts.relations import RelationCapability
|
|
15
|
+
from trackerkit.contracts.task_tracker_client import TaskTrackerClient
|
|
16
|
+
from trackerkit.contracts.tasks import TaskReadCapability, TaskWriteCapability
|
|
17
|
+
from trackerkit.contracts.users import UserCapability
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"AsanaAuthConfig",
|
|
21
|
+
"CommentCapability",
|
|
22
|
+
"ConnectionCapability",
|
|
23
|
+
"JiraAuthConfig",
|
|
24
|
+
"ProjectReadCapability",
|
|
25
|
+
"ProjectWriteCapability",
|
|
26
|
+
"ProviderAuthConfig",
|
|
27
|
+
"RelationCapability",
|
|
28
|
+
"TaskReadCapability",
|
|
29
|
+
"TaskTrackerClient",
|
|
30
|
+
"TaskWriteCapability",
|
|
31
|
+
"UserCapability",
|
|
32
|
+
"WorkspaceCapability",
|
|
33
|
+
"YandexTrackerAuthConfig",
|
|
34
|
+
]
|
|
35
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
2
|
+
|
|
3
|
+
from trackerkit.domain.enums import Provider
|
|
4
|
+
from trackerkit.domain.errors import ConfigurationError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ProviderAuthConfig(BaseModel):
|
|
8
|
+
"""Base auth config shared by all provider-specific auth models."""
|
|
9
|
+
|
|
10
|
+
# `extra="ignore"` lets the facade pass provider-agnostic kwargs
|
|
11
|
+
# (e.g. `max_retries`) without leaking provider checks into it.
|
|
12
|
+
model_config = ConfigDict(frozen=True, extra="ignore")
|
|
13
|
+
|
|
14
|
+
provider: Provider
|
|
15
|
+
timeout_seconds: float = Field(default=5.0, gt=0)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class JiraAuthConfig(ProviderAuthConfig):
|
|
19
|
+
"""Jira auth data.
|
|
20
|
+
|
|
21
|
+
Expected fields:
|
|
22
|
+
- `base_url`
|
|
23
|
+
- exactly one of `access_token` or `api_token`
|
|
24
|
+
- optional `max_retries`
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
provider: Provider = Provider.JIRA
|
|
28
|
+
base_url: str
|
|
29
|
+
email: str | None = None
|
|
30
|
+
api_token: str | None = None
|
|
31
|
+
access_token: str | None = None
|
|
32
|
+
max_retries: int = Field(default=2, ge=0)
|
|
33
|
+
|
|
34
|
+
@model_validator(mode="after")
|
|
35
|
+
def validate_auth(self) -> "JiraAuthConfig":
|
|
36
|
+
if not self.api_token and not self.access_token:
|
|
37
|
+
raise ConfigurationError(
|
|
38
|
+
"JiraAuthConfig requires either api_token or access_token."
|
|
39
|
+
)
|
|
40
|
+
if self.api_token and self.access_token:
|
|
41
|
+
raise ConfigurationError(
|
|
42
|
+
"JiraAuthConfig expects only one of api_token or access_token."
|
|
43
|
+
)
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class YandexTrackerAuthConfig(ProviderAuthConfig):
|
|
48
|
+
"""Yandex Tracker auth data.
|
|
49
|
+
|
|
50
|
+
Expected fields:
|
|
51
|
+
- exactly one of `token` or `iam_token`
|
|
52
|
+
- exactly one of `org_id` or `cloud_org_id`
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
provider: Provider = Provider.YANDEX_TRACKER
|
|
56
|
+
token: str | None = None
|
|
57
|
+
iam_token: str | None = None
|
|
58
|
+
org_id: str | None = None
|
|
59
|
+
cloud_org_id: str | None = None
|
|
60
|
+
|
|
61
|
+
@model_validator(mode="after")
|
|
62
|
+
def validate_auth(self) -> "YandexTrackerAuthConfig":
|
|
63
|
+
if not self.token and not self.iam_token:
|
|
64
|
+
raise ConfigurationError(
|
|
65
|
+
"YandexTrackerAuthConfig requires either token or iam_token."
|
|
66
|
+
)
|
|
67
|
+
if self.token and self.iam_token:
|
|
68
|
+
raise ConfigurationError(
|
|
69
|
+
"YandexTrackerAuthConfig expects only one of token or iam_token."
|
|
70
|
+
)
|
|
71
|
+
if not self.org_id and not self.cloud_org_id:
|
|
72
|
+
raise ConfigurationError(
|
|
73
|
+
"YandexTrackerAuthConfig requires either org_id or cloud_org_id."
|
|
74
|
+
)
|
|
75
|
+
if self.org_id and self.cloud_org_id:
|
|
76
|
+
raise ConfigurationError(
|
|
77
|
+
"YandexTrackerAuthConfig expects only one of org_id or cloud_org_id."
|
|
78
|
+
)
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AsanaAuthConfig(ProviderAuthConfig):
|
|
83
|
+
"""Asana auth data.
|
|
84
|
+
|
|
85
|
+
Expected fields:
|
|
86
|
+
- `access_token`
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
provider: Provider = Provider.ASANA
|
|
90
|
+
access_token: str
|
|
91
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from trackerkit.domain.models import Comment, CreateCommentInput
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CommentCapability(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def list_comments(self, task_id: str) -> list[Comment]:
|
|
9
|
+
raise NotImplementedError
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def create_comment(self, payload: CreateCommentInput) -> Comment:
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from trackerkit.domain.models import ConnectionDiagnostic
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConnectionCapability(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def check_connection(self) -> bool:
|
|
9
|
+
raise NotImplementedError
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def get_connection_diagnostic(self) -> ConnectionDiagnostic:
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from trackerkit.domain.models import (
|
|
4
|
+
CreateProjectInput,
|
|
5
|
+
Project,
|
|
6
|
+
UpdateProjectInput,
|
|
7
|
+
Workspace,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WorkspaceCapability(ABC):
|
|
12
|
+
@abstractmethod
|
|
13
|
+
async def list_workspaces(self) -> list[Workspace]:
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProjectReadCapability(ABC):
|
|
18
|
+
@abstractmethod
|
|
19
|
+
async def get_project(self, project_id: str) -> Project:
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
async def list_projects(self, workspace_id: str | None = None) -> list[Project]:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ProjectWriteCapability(ABC):
|
|
28
|
+
@abstractmethod
|
|
29
|
+
async def create_project(self, payload: CreateProjectInput) -> Project:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
async def update_project(
|
|
34
|
+
self,
|
|
35
|
+
project_id: str,
|
|
36
|
+
payload: UpdateProjectInput,
|
|
37
|
+
) -> Project:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def delete_project(self, project_id: str) -> None:
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from trackerkit.domain.models import (
|
|
4
|
+
CreateRelationInput,
|
|
5
|
+
Relation,
|
|
6
|
+
UpdateRelationInput,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RelationCapability(ABC):
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def list_relations(self, task_id: str) -> list[Relation]:
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
async def create_relation(self, payload: CreateRelationInput) -> Relation:
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
async def update_relation(
|
|
21
|
+
self,
|
|
22
|
+
relation_id: str,
|
|
23
|
+
payload: UpdateRelationInput,
|
|
24
|
+
) -> Relation:
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
async def delete_relation(self, relation_id: str) -> None:
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from trackerkit.contracts.comments import CommentCapability
|
|
2
|
+
from trackerkit.contracts.connection import ConnectionCapability
|
|
3
|
+
from trackerkit.contracts.projects import (
|
|
4
|
+
ProjectReadCapability,
|
|
5
|
+
ProjectWriteCapability,
|
|
6
|
+
WorkspaceCapability,
|
|
7
|
+
)
|
|
8
|
+
from trackerkit.contracts.relations import RelationCapability
|
|
9
|
+
from trackerkit.contracts.tasks import TaskReadCapability, TaskWriteCapability
|
|
10
|
+
from trackerkit.contracts.users import UserCapability
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TaskTrackerClient(
|
|
14
|
+
ConnectionCapability,
|
|
15
|
+
TaskReadCapability,
|
|
16
|
+
TaskWriteCapability,
|
|
17
|
+
WorkspaceCapability,
|
|
18
|
+
ProjectReadCapability,
|
|
19
|
+
ProjectWriteCapability,
|
|
20
|
+
UserCapability,
|
|
21
|
+
CommentCapability,
|
|
22
|
+
RelationCapability,
|
|
23
|
+
):
|
|
24
|
+
"""Unified async port composed from small capability contracts."""
|
|
25
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from trackerkit.domain.models import CreateTaskInput, Task, TaskQuery, UpdateTaskInput
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TaskReadCapability(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def get_task(self, task_id: str) -> Task:
|
|
9
|
+
raise NotImplementedError
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
async def list_tasks(self, query: TaskQuery | None = None) -> list[Task]:
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TaskWriteCapability(ABC):
|
|
17
|
+
@abstractmethod
|
|
18
|
+
async def create_task(self, payload: CreateTaskInput) -> Task:
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def update_task(self, task_id: str, payload: UpdateTaskInput) -> Task:
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
async def delete_task(self, task_id: str) -> None:
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from trackerkit.domain.enums import ConnectionErrorKind, Provider, RelationType
|
|
2
|
+
from trackerkit.domain.errors import (
|
|
3
|
+
AuthenticationError,
|
|
4
|
+
ConfigurationError,
|
|
5
|
+
ProviderCapabilityError,
|
|
6
|
+
ProviderError,
|
|
7
|
+
TrackerClientError,
|
|
8
|
+
TrackerKitError,
|
|
9
|
+
)
|
|
10
|
+
from trackerkit.domain.models import (
|
|
11
|
+
Comment,
|
|
12
|
+
ConnectionDiagnostic,
|
|
13
|
+
CreateCommentInput,
|
|
14
|
+
CreateProjectInput,
|
|
15
|
+
CreateRelationInput,
|
|
16
|
+
CreateTaskInput,
|
|
17
|
+
Project,
|
|
18
|
+
Relation,
|
|
19
|
+
Status,
|
|
20
|
+
Task,
|
|
21
|
+
TaskQuery,
|
|
22
|
+
UpdateProjectInput,
|
|
23
|
+
UpdateRelationInput,
|
|
24
|
+
UpdateTaskInput,
|
|
25
|
+
User,
|
|
26
|
+
Workspace,
|
|
27
|
+
)
|
|
28
|
+
from trackerkit.domain.relation_mapping import (
|
|
29
|
+
JiraContainsMode,
|
|
30
|
+
JiraLinkTypeMapping,
|
|
31
|
+
JiraRelationMappingConfig,
|
|
32
|
+
RelationMappingConfig,
|
|
33
|
+
YandexTrackerRelationMapping,
|
|
34
|
+
YandexTrackerRelationMappingConfig,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"AuthenticationError",
|
|
39
|
+
"Comment",
|
|
40
|
+
"ConfigurationError",
|
|
41
|
+
"ConnectionDiagnostic",
|
|
42
|
+
"ConnectionErrorKind",
|
|
43
|
+
"CreateCommentInput",
|
|
44
|
+
"CreateProjectInput",
|
|
45
|
+
"CreateRelationInput",
|
|
46
|
+
"CreateTaskInput",
|
|
47
|
+
"JiraContainsMode",
|
|
48
|
+
"JiraLinkTypeMapping",
|
|
49
|
+
"JiraRelationMappingConfig",
|
|
50
|
+
"Project",
|
|
51
|
+
"Provider",
|
|
52
|
+
"ProviderCapabilityError",
|
|
53
|
+
"ProviderError",
|
|
54
|
+
"Relation",
|
|
55
|
+
"RelationMappingConfig",
|
|
56
|
+
"RelationType",
|
|
57
|
+
"Status",
|
|
58
|
+
"Task",
|
|
59
|
+
"TaskQuery",
|
|
60
|
+
"TrackerClientError",
|
|
61
|
+
"TrackerKitError",
|
|
62
|
+
"UpdateProjectInput",
|
|
63
|
+
"UpdateRelationInput",
|
|
64
|
+
"UpdateTaskInput",
|
|
65
|
+
"User",
|
|
66
|
+
"Workspace",
|
|
67
|
+
"YandexTrackerRelationMapping",
|
|
68
|
+
"YandexTrackerRelationMappingConfig",
|
|
69
|
+
]
|
|
70
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Provider(str, Enum):
|
|
5
|
+
JIRA = "jira"
|
|
6
|
+
YANDEX_TRACKER = "yandex_tracker"
|
|
7
|
+
ASANA = "asana"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RelationType(str, Enum):
|
|
11
|
+
RELATES = "relates"
|
|
12
|
+
BLOCKS = "blocks"
|
|
13
|
+
CONTAINS = "contains"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConnectionErrorKind(str, Enum):
|
|
17
|
+
"""Stable taxonomy of connection-check failure reasons.
|
|
18
|
+
|
|
19
|
+
`ConnectionDiagnostic.error_kind` uses these values; the string base
|
|
20
|
+
keeps backward compatibility with callers that compare against literals
|
|
21
|
+
like ``"authentication"``.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
AUTHENTICATION = "authentication"
|
|
25
|
+
CONFIGURATION = "configuration"
|
|
26
|
+
CAPABILITY = "capability"
|
|
27
|
+
PROVIDER = "provider"
|
|
28
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from trackerkit.domain.enums import ConnectionErrorKind
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TrackerClientError(Exception):
|
|
5
|
+
"""Base exception for the package."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ConfigurationError(TrackerClientError):
|
|
9
|
+
"""Raised when client configuration is invalid."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AuthenticationError(TrackerClientError):
|
|
13
|
+
"""Raised when provider authentication fails."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ProviderError(TrackerClientError):
|
|
17
|
+
"""Raised when a provider-specific operation fails."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ProviderCapabilityError(TrackerClientError):
|
|
21
|
+
"""Raised when a provider does not support a requested capability."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
TrackerKitError = TrackerClientError
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_error_kind(error: Exception) -> ConnectionErrorKind:
|
|
28
|
+
if isinstance(error, AuthenticationError):
|
|
29
|
+
return ConnectionErrorKind.AUTHENTICATION
|
|
30
|
+
if isinstance(error, ConfigurationError):
|
|
31
|
+
return ConnectionErrorKind.CONFIGURATION
|
|
32
|
+
if isinstance(error, ProviderCapabilityError):
|
|
33
|
+
return ConnectionErrorKind.CAPABILITY
|
|
34
|
+
if isinstance(error, ProviderError):
|
|
35
|
+
return ConnectionErrorKind.PROVIDER
|
|
36
|
+
return ConnectionErrorKind.PROVIDER
|
|
37
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from trackerkit.domain.enums import ConnectionErrorKind, Provider, RelationType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class User(BaseModel):
|
|
9
|
+
id: str
|
|
10
|
+
display_name: str
|
|
11
|
+
email: str | None = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Workspace(BaseModel):
|
|
15
|
+
id: str
|
|
16
|
+
name: str
|
|
17
|
+
key: str | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConnectionDiagnostic(BaseModel):
|
|
21
|
+
provider: Provider
|
|
22
|
+
is_connected: bool
|
|
23
|
+
# Pydantic accepts both the enum and its string value (it's a `str` enum),
|
|
24
|
+
# so existing callers passing literals like ``"authentication"`` keep working.
|
|
25
|
+
error_kind: ConnectionErrorKind | None = None
|
|
26
|
+
message: str | None = None
|
|
27
|
+
error_type: str | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Project(BaseModel):
|
|
31
|
+
id: str
|
|
32
|
+
name: str
|
|
33
|
+
key: str | None = None
|
|
34
|
+
description: str | None = None
|
|
35
|
+
workspace_id: str | None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Status(BaseModel):
|
|
39
|
+
id: str
|
|
40
|
+
name: str
|
|
41
|
+
category: str | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Task(BaseModel):
|
|
45
|
+
id: str
|
|
46
|
+
key: str | None = None
|
|
47
|
+
title: str
|
|
48
|
+
description: str | None = None
|
|
49
|
+
project_id: str | None = None
|
|
50
|
+
status: Status | None = None
|
|
51
|
+
assignee: User | None = None
|
|
52
|
+
reporter: User | None = None
|
|
53
|
+
created_at: datetime | None = None
|
|
54
|
+
updated_at: datetime | None = None
|
|
55
|
+
due_date: datetime | None = None
|
|
56
|
+
url: str | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Comment(BaseModel):
|
|
60
|
+
id: str
|
|
61
|
+
task_id: str
|
|
62
|
+
body: str
|
|
63
|
+
author: User | None = None
|
|
64
|
+
created_at: datetime | None = None
|
|
65
|
+
updated_at: datetime | None = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Relation(BaseModel):
|
|
69
|
+
id: str | None = None
|
|
70
|
+
source_task_id: str
|
|
71
|
+
target_task_id: str
|
|
72
|
+
relation_type: RelationType
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TaskQuery(BaseModel):
|
|
76
|
+
project_id: str | None = None
|
|
77
|
+
assignee_id: str | None = None
|
|
78
|
+
status_id: str | None = None
|
|
79
|
+
updated_since: datetime | None = None
|
|
80
|
+
# Soft cap on the number of items the adapter materializes from a paginated
|
|
81
|
+
# provider response. Currently honored by Asana (`list_tasks`); Jira/Yandex
|
|
82
|
+
# ignore it because they already use bounded server-side queries.
|
|
83
|
+
limit: int | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CreateTaskInput(BaseModel):
|
|
87
|
+
title: str
|
|
88
|
+
description: str | None = None
|
|
89
|
+
project_id: str | None = None
|
|
90
|
+
assignee_id: str | None = None
|
|
91
|
+
status_id: str | None = None
|
|
92
|
+
due_date: datetime | None = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class UpdateTaskInput(BaseModel):
|
|
96
|
+
title: str | None = None
|
|
97
|
+
description: str | None = None
|
|
98
|
+
assignee_id: str | None = None
|
|
99
|
+
status_id: str | None = None
|
|
100
|
+
due_date: datetime | None = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class CreateCommentInput(BaseModel):
|
|
104
|
+
task_id: str
|
|
105
|
+
body: str
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class CreateProjectInput(BaseModel):
|
|
109
|
+
name: str
|
|
110
|
+
key: str | None = None
|
|
111
|
+
description: str | None = None
|
|
112
|
+
workspace_id: str | None = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class UpdateProjectInput(BaseModel):
|
|
116
|
+
name: str | None = None
|
|
117
|
+
key: str | None = None
|
|
118
|
+
description: str | None = None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CreateRelationInput(BaseModel):
|
|
122
|
+
source_task_id: str
|
|
123
|
+
target_task_id: str
|
|
124
|
+
relation_type: RelationType
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class UpdateRelationInput(BaseModel):
|
|
128
|
+
source_task_id: str
|
|
129
|
+
target_task_id: str
|
|
130
|
+
relation_type: RelationType
|
|
131
|
+
|