linear-python-client 0.1.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.
- linear_python_client/__init__.py +147 -0
- linear_python_client/client.py +627 -0
- linear_python_client/errors.py +78 -0
- linear_python_client/graphql/__init__.py +10 -0
- linear_python_client/graphql/queries.py +405 -0
- linear_python_client/models/__init__.py +132 -0
- linear_python_client/models/entities.py +187 -0
- linear_python_client/models/requests.py +260 -0
- linear_python_client/models/responses.py +169 -0
- linear_python_client/py.typed +0 -0
- linear_python_client-0.1.0.dist-info/METADATA +338 -0
- linear_python_client-0.1.0.dist-info/RECORD +14 -0
- linear_python_client-0.1.0.dist-info/WHEEL +4 -0
- linear_python_client-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Pydantic models for Linear API entities.
|
|
2
|
+
|
|
3
|
+
All models use snake_case attribute names with automatically generated camelCase
|
|
4
|
+
aliases, so they parse the API's camelCase payloads (`displayName`, `createdAt`,
|
|
5
|
+
…) and can be serialised back to them with `model_dump(by_alias=True)`. Because
|
|
6
|
+
`populate_by_name=True`, you can construct them with either spelling.
|
|
7
|
+
|
|
8
|
+
Only the fields a given query requested are populated; everything is optional and
|
|
9
|
+
defaults to `None`, and unknown fields are ignored.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
17
|
+
from pydantic.alias_generators import to_camel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _unwrap_nodes(value: object) -> object:
|
|
21
|
+
"""Accept Linear's `{ nodes: [...] }` connection shape, returning the node list."""
|
|
22
|
+
if isinstance(value, dict) and "nodes" in value:
|
|
23
|
+
return value["nodes"]
|
|
24
|
+
return value
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LinearModel(BaseModel):
|
|
28
|
+
"""Base model: camelCase aliases, snake_case attributes, lenient parsing."""
|
|
29
|
+
|
|
30
|
+
model_config = ConfigDict(
|
|
31
|
+
alias_generator=to_camel,
|
|
32
|
+
populate_by_name=True,
|
|
33
|
+
extra="ignore",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PageInfo(LinearModel):
|
|
38
|
+
"""Relay-style pagination metadata for a connection."""
|
|
39
|
+
|
|
40
|
+
has_next_page: bool = False
|
|
41
|
+
has_previous_page: bool = False
|
|
42
|
+
start_cursor: str | None = None
|
|
43
|
+
end_cursor: str | None = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class User(LinearModel):
|
|
47
|
+
"""A Linear user."""
|
|
48
|
+
|
|
49
|
+
id: str | None = None
|
|
50
|
+
name: str | None = None
|
|
51
|
+
display_name: str | None = None
|
|
52
|
+
email: str | None = None
|
|
53
|
+
active: bool | None = None
|
|
54
|
+
admin: bool | None = None
|
|
55
|
+
created_at: datetime | None = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Team(LinearModel):
|
|
59
|
+
"""A Linear team."""
|
|
60
|
+
|
|
61
|
+
id: str | None = None
|
|
62
|
+
name: str | None = None
|
|
63
|
+
key: str | None = None
|
|
64
|
+
description: str | None = None
|
|
65
|
+
private: bool | None = None
|
|
66
|
+
created_at: datetime | None = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class WorkflowState(LinearModel):
|
|
70
|
+
"""An issue workflow state (e.g. Todo, In Progress, Done)."""
|
|
71
|
+
|
|
72
|
+
id: str | None = None
|
|
73
|
+
name: str | None = None
|
|
74
|
+
type: str | None = None
|
|
75
|
+
color: str | None = None
|
|
76
|
+
position: float | None = None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class IssueLabel(LinearModel):
|
|
80
|
+
"""A label that can be applied to issues."""
|
|
81
|
+
|
|
82
|
+
id: str | None = None
|
|
83
|
+
name: str | None = None
|
|
84
|
+
color: str | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Project(LinearModel):
|
|
88
|
+
"""A Linear project."""
|
|
89
|
+
|
|
90
|
+
id: str | None = None
|
|
91
|
+
name: str | None = None
|
|
92
|
+
description: str | None = None
|
|
93
|
+
slug_id: str | None = None
|
|
94
|
+
state: str | None = None
|
|
95
|
+
progress: float | None = None
|
|
96
|
+
created_at: datetime | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Comment(LinearModel):
|
|
100
|
+
"""A comment on an issue."""
|
|
101
|
+
|
|
102
|
+
id: str | None = None
|
|
103
|
+
body: str | None = None
|
|
104
|
+
url: str | None = None
|
|
105
|
+
user: User | None = None
|
|
106
|
+
created_at: datetime | None = None
|
|
107
|
+
updated_at: datetime | None = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Issue(LinearModel):
|
|
111
|
+
"""A Linear issue, with nested relations populated when requested."""
|
|
112
|
+
|
|
113
|
+
id: str | None = None
|
|
114
|
+
identifier: str | None = None
|
|
115
|
+
title: str | None = None
|
|
116
|
+
description: str | None = None
|
|
117
|
+
url: str | None = None
|
|
118
|
+
priority: int | None = None
|
|
119
|
+
estimate: float | None = None
|
|
120
|
+
branch_name: str | None = None
|
|
121
|
+
created_at: datetime | None = None
|
|
122
|
+
updated_at: datetime | None = None
|
|
123
|
+
completed_at: datetime | None = None
|
|
124
|
+
assignee: User | None = None
|
|
125
|
+
creator: User | None = None
|
|
126
|
+
team: Team | None = None
|
|
127
|
+
state: WorkflowState | None = None
|
|
128
|
+
labels: list[IssueLabel] = Field(default_factory=list)
|
|
129
|
+
|
|
130
|
+
@field_validator("labels", mode="before")
|
|
131
|
+
@classmethod
|
|
132
|
+
def _unwrap_label_nodes(cls, value: object) -> object:
|
|
133
|
+
"""Accept Linear's `labels: { nodes: [...] }` connection shape."""
|
|
134
|
+
return _unwrap_nodes(value)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class Attachment(LinearModel):
|
|
138
|
+
"""A link or file attached to an issue."""
|
|
139
|
+
|
|
140
|
+
id: str | None = None
|
|
141
|
+
title: str | None = None
|
|
142
|
+
subtitle: str | None = None
|
|
143
|
+
url: str | None = None
|
|
144
|
+
created_at: datetime | None = None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class Cycle(LinearModel):
|
|
148
|
+
"""A team cycle (sprint)."""
|
|
149
|
+
|
|
150
|
+
id: str | None = None
|
|
151
|
+
number: int | None = None
|
|
152
|
+
name: str | None = None
|
|
153
|
+
starts_at: datetime | None = None
|
|
154
|
+
ends_at: datetime | None = None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class IssueRelation(LinearModel):
|
|
158
|
+
"""A relation from an issue to another (e.g. blocks, related, duplicate)."""
|
|
159
|
+
|
|
160
|
+
type: str | None = None
|
|
161
|
+
related_issue: Issue | None = None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class IssueDetail(Issue):
|
|
165
|
+
"""An issue plus its heavier related data, returned by `issue_details`.
|
|
166
|
+
|
|
167
|
+
Inherits every field of [`Issue`][linear_python_client.Issue] and adds the
|
|
168
|
+
related collections. As always, only the fields the query requested are
|
|
169
|
+
populated; `parent`, `children`, and relation targets are shallow issues.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
comments: list[Comment] = Field(default_factory=list)
|
|
173
|
+
attachments: list[Attachment] = Field(default_factory=list)
|
|
174
|
+
project: Project | None = None
|
|
175
|
+
cycle: Cycle | None = None
|
|
176
|
+
parent: Issue | None = None
|
|
177
|
+
children: list[Issue] = Field(default_factory=list)
|
|
178
|
+
subscribers: list[User] = Field(default_factory=list)
|
|
179
|
+
relations: list[IssueRelation] = Field(default_factory=list)
|
|
180
|
+
|
|
181
|
+
@field_validator(
|
|
182
|
+
"comments", "attachments", "children", "subscribers", "relations", mode="before"
|
|
183
|
+
)
|
|
184
|
+
@classmethod
|
|
185
|
+
def _unwrap_connection_nodes(cls, value: object) -> object:
|
|
186
|
+
"""Accept Linear's `{ nodes: [...] }` connection shape for the collections."""
|
|
187
|
+
return _unwrap_nodes(value)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Typed request models for each :class:`~linear_python_client.client.LinearClient` call.
|
|
2
|
+
|
|
3
|
+
Every client method takes exactly one of these. They carry snake_case fields with
|
|
4
|
+
camelCase aliases, and expose helpers (`to_variables`, `to_input`) that serialise
|
|
5
|
+
them into the GraphQL variables the API expects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pydantic import ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
from .entities import LinearModel
|
|
15
|
+
|
|
16
|
+
# -- query requests ---------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IssueRequest(LinearModel):
|
|
20
|
+
"""Fetch a single issue by id or human identifier (e.g. `"ENG-123"`)."""
|
|
21
|
+
|
|
22
|
+
id: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class UserRequest(LinearModel):
|
|
26
|
+
"""Fetch a single user by UUID."""
|
|
27
|
+
|
|
28
|
+
id: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TeamRequest(LinearModel):
|
|
32
|
+
"""Fetch a single team by UUID."""
|
|
33
|
+
|
|
34
|
+
id: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ProjectRequest(LinearModel):
|
|
38
|
+
"""Fetch a single project by UUID."""
|
|
39
|
+
|
|
40
|
+
id: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CommentRequest(LinearModel):
|
|
44
|
+
"""Fetch a single comment by UUID."""
|
|
45
|
+
|
|
46
|
+
id: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class PaginatedRequest(LinearModel):
|
|
50
|
+
"""Base for list requests: cursor pagination plus an optional filter.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
first: Maximum number of results to return (Linear defaults to 50).
|
|
54
|
+
after: Pagination cursor; pass the previous page's `end_cursor`.
|
|
55
|
+
filter: A [Linear filter](https://linear.app/developers/filtering) dict.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
first: int | None = None
|
|
59
|
+
after: str | None = None
|
|
60
|
+
filter: dict[str, Any] | None = None
|
|
61
|
+
|
|
62
|
+
def to_variables(self) -> dict[str, Any]:
|
|
63
|
+
"""Serialise to GraphQL variables (camelCase, omitting unset values)."""
|
|
64
|
+
return self.model_dump(by_alias=True, exclude_none=True)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class IssuesRequest(PaginatedRequest):
|
|
68
|
+
"""List issues, optionally filtered and ordered.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
order_by: Sort order, either `"createdAt"` (default) or `"updatedAt"`.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
order_by: str | None = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class UsersRequest(PaginatedRequest):
|
|
78
|
+
"""List users in the workspace."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TeamsRequest(PaginatedRequest):
|
|
82
|
+
"""List teams in the workspace."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ProjectsRequest(PaginatedRequest):
|
|
86
|
+
"""List projects in the workspace."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class IssueLabelsRequest(PaginatedRequest):
|
|
90
|
+
"""List issue labels in the workspace."""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class CommentsRequest(PaginatedRequest):
|
|
94
|
+
"""List comments, optionally scoped to a single issue.
|
|
95
|
+
|
|
96
|
+
Attributes:
|
|
97
|
+
issue_id: When set, only comments on this issue are returned (merged into
|
|
98
|
+
`filter`). Not sent as a raw variable.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
issue_id: str | None = Field(default=None, exclude=True)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class WorkflowStatesRequest(PaginatedRequest):
|
|
105
|
+
"""List workflow states, optionally scoped to a single team.
|
|
106
|
+
|
|
107
|
+
Attributes:
|
|
108
|
+
team_id: When set, only states belonging to this team are returned (merged
|
|
109
|
+
into `filter`). Not sent as a raw variable.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
team_id: str | None = Field(default=None, exclude=True)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# -- mutation requests ------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class IssueCreateRequest(LinearModel):
|
|
119
|
+
"""Input for creating an issue.
|
|
120
|
+
|
|
121
|
+
Any field accepted by Linear's `IssueCreateInput` may also be passed as an
|
|
122
|
+
extra keyword argument using its camelCase API name (e.g. `dueDate="2026-01-01"`).
|
|
123
|
+
|
|
124
|
+
Attributes:
|
|
125
|
+
team_id: UUID of the team the issue belongs to (required).
|
|
126
|
+
title: The issue title (required).
|
|
127
|
+
description: Markdown body for the issue.
|
|
128
|
+
assignee_id: UUID of the user to assign.
|
|
129
|
+
state_id: UUID of the workflow state to set.
|
|
130
|
+
priority: Priority from 0 (none) to 4 (low); 1 is urgent.
|
|
131
|
+
label_ids: UUIDs of labels to attach.
|
|
132
|
+
project_id: UUID of the project to add the issue to.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
model_config = ConfigDict(extra="allow")
|
|
136
|
+
|
|
137
|
+
team_id: str
|
|
138
|
+
title: str
|
|
139
|
+
description: str | None = None
|
|
140
|
+
assignee_id: str | None = None
|
|
141
|
+
state_id: str | None = None
|
|
142
|
+
priority: int | None = None
|
|
143
|
+
label_ids: list[str] | None = None
|
|
144
|
+
project_id: str | None = None
|
|
145
|
+
|
|
146
|
+
def to_input(self) -> dict[str, Any]:
|
|
147
|
+
"""Serialise to an `IssueCreateInput` dict (camelCase, omitting unset values)."""
|
|
148
|
+
return self.model_dump(by_alias=True, exclude_none=True)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class IssueUpdateRequest(LinearModel):
|
|
152
|
+
"""Input for updating an issue.
|
|
153
|
+
|
|
154
|
+
At least one field other than `id` must be set. Any field accepted by Linear's
|
|
155
|
+
`IssueUpdateInput` may also be passed as an extra keyword argument using its
|
|
156
|
+
camelCase API name.
|
|
157
|
+
|
|
158
|
+
Attributes:
|
|
159
|
+
id: UUID of the issue to update (required).
|
|
160
|
+
title: New title.
|
|
161
|
+
description: New markdown body.
|
|
162
|
+
assignee_id: UUID of the user to assign.
|
|
163
|
+
state_id: UUID of the workflow state to set.
|
|
164
|
+
priority: Priority from 0 (none) to 4 (low); 1 is urgent.
|
|
165
|
+
label_ids: UUIDs of labels to set.
|
|
166
|
+
project_id: UUID of the project to move the issue to.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
model_config = ConfigDict(extra="allow")
|
|
170
|
+
|
|
171
|
+
id: str
|
|
172
|
+
title: str | None = None
|
|
173
|
+
description: str | None = None
|
|
174
|
+
assignee_id: str | None = None
|
|
175
|
+
state_id: str | None = None
|
|
176
|
+
priority: int | None = None
|
|
177
|
+
label_ids: list[str] | None = None
|
|
178
|
+
project_id: str | None = None
|
|
179
|
+
|
|
180
|
+
def to_input(self) -> dict[str, Any]:
|
|
181
|
+
"""Serialise the update fields to an `IssueUpdateInput` dict (excluding `id`)."""
|
|
182
|
+
data = self.model_dump(by_alias=True, exclude_none=True)
|
|
183
|
+
data.pop("id", None)
|
|
184
|
+
return data
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class IssueArchiveRequest(LinearModel):
|
|
188
|
+
"""Archive an issue by UUID."""
|
|
189
|
+
|
|
190
|
+
id: str
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class IssueAddLabelRequest(LinearModel):
|
|
194
|
+
"""Add a single label to an issue, leaving its other labels untouched.
|
|
195
|
+
|
|
196
|
+
Attributes:
|
|
197
|
+
id: UUID of the issue.
|
|
198
|
+
label_id: UUID of the label to add.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
id: str
|
|
202
|
+
label_id: str
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class IssueRemoveLabelRequest(LinearModel):
|
|
206
|
+
"""Remove a single label from an issue, leaving its other labels untouched.
|
|
207
|
+
|
|
208
|
+
Attributes:
|
|
209
|
+
id: UUID of the issue.
|
|
210
|
+
label_id: UUID of the label to remove.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
id: str
|
|
214
|
+
label_id: str
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class IssueSetStateRequest(LinearModel):
|
|
218
|
+
"""Move an issue to a workflow state (status).
|
|
219
|
+
|
|
220
|
+
Attributes:
|
|
221
|
+
id: UUID of the issue.
|
|
222
|
+
state_id: UUID of the target workflow state. Resolve one by name with
|
|
223
|
+
[`find_workflow_state`][linear_python_client.client.LinearClient.find_workflow_state].
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
id: str
|
|
227
|
+
state_id: str
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class FindWorkflowStateRequest(LinearModel):
|
|
231
|
+
"""Resolve a workflow state by name within a team.
|
|
232
|
+
|
|
233
|
+
Attributes:
|
|
234
|
+
team_id: UUID of the team that owns the state.
|
|
235
|
+
name: State name to match, case-insensitively (e.g. `"In Progress"`).
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
team_id: str
|
|
239
|
+
name: str
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class CommentCreateRequest(LinearModel):
|
|
243
|
+
"""Input for adding a comment to an issue.
|
|
244
|
+
|
|
245
|
+
Any field accepted by Linear's `CommentCreateInput` may also be passed as an
|
|
246
|
+
extra keyword argument using its camelCase API name.
|
|
247
|
+
|
|
248
|
+
Attributes:
|
|
249
|
+
issue_id: UUID of the issue to comment on (required).
|
|
250
|
+
body: Markdown body of the comment (required).
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
model_config = ConfigDict(extra="allow")
|
|
254
|
+
|
|
255
|
+
issue_id: str
|
|
256
|
+
body: str
|
|
257
|
+
|
|
258
|
+
def to_input(self) -> dict[str, Any]:
|
|
259
|
+
"""Serialise to a `CommentCreateInput` dict (camelCase, omitting unset values)."""
|
|
260
|
+
return self.model_dump(by_alias=True, exclude_none=True)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Typed response models returned by each :class:`~linear_python_client.client.LinearClient` call.
|
|
2
|
+
|
|
3
|
+
Single-entity queries return a wrapper exposing the entity (e.g.
|
|
4
|
+
:class:`IssueResponse.issue`). List queries return a :class:`ConnectionResponse`
|
|
5
|
+
subclass exposing `nodes` and `page_info`. Mutations mirror the GraphQL payload,
|
|
6
|
+
exposing `success` alongside the affected entity.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
|
|
13
|
+
from .entities import (
|
|
14
|
+
Comment,
|
|
15
|
+
Issue,
|
|
16
|
+
IssueDetail,
|
|
17
|
+
IssueLabel,
|
|
18
|
+
LinearModel,
|
|
19
|
+
PageInfo,
|
|
20
|
+
Project,
|
|
21
|
+
Team,
|
|
22
|
+
User,
|
|
23
|
+
WorkflowState,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ConnectionResponse[NodeT](LinearModel):
|
|
28
|
+
"""Base for list responses: a page of `nodes` plus its :class:`PageInfo`.
|
|
29
|
+
|
|
30
|
+
Iterable and sized, so you can loop over the response directly or read
|
|
31
|
+
`response.nodes` / `response.page_info`.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
nodes: list[NodeT] = Field(default_factory=list)
|
|
35
|
+
page_info: PageInfo = Field(default_factory=PageInfo)
|
|
36
|
+
|
|
37
|
+
def __iter__(self):
|
|
38
|
+
"""Iterate over `nodes`."""
|
|
39
|
+
return iter(self.nodes)
|
|
40
|
+
|
|
41
|
+
def __len__(self) -> int:
|
|
42
|
+
"""Number of nodes on this page."""
|
|
43
|
+
return len(self.nodes)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# -- single-entity query responses -----------------------------------------
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ViewerResponse(LinearModel):
|
|
50
|
+
"""Response for [`viewer`][linear_python_client.client.LinearClient.viewer]."""
|
|
51
|
+
|
|
52
|
+
viewer: User | None = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class UserResponse(LinearModel):
|
|
56
|
+
"""Response for [`user`][linear_python_client.client.LinearClient.user]."""
|
|
57
|
+
|
|
58
|
+
user: User | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TeamResponse(LinearModel):
|
|
62
|
+
"""Response for [`team`][linear_python_client.client.LinearClient.team]."""
|
|
63
|
+
|
|
64
|
+
team: Team | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class IssueResponse(LinearModel):
|
|
68
|
+
"""Response for [`issue`][linear_python_client.client.LinearClient.issue]."""
|
|
69
|
+
|
|
70
|
+
issue: Issue | None = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class IssueDetailsResponse(LinearModel):
|
|
74
|
+
"""Response for [`issue_details`][linear_python_client.client.LinearClient.issue_details]."""
|
|
75
|
+
|
|
76
|
+
issue: IssueDetail | None = None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class WorkflowStateResponse(LinearModel):
|
|
80
|
+
"""Resolved workflow state from `find_workflow_state` (`None` if no match)."""
|
|
81
|
+
|
|
82
|
+
state: WorkflowState | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ProjectResponse(LinearModel):
|
|
86
|
+
"""Response for [`project`][linear_python_client.client.LinearClient.project]."""
|
|
87
|
+
|
|
88
|
+
project: Project | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CommentResponse(LinearModel):
|
|
92
|
+
"""Response for [`comment`][linear_python_client.client.LinearClient.comment]."""
|
|
93
|
+
|
|
94
|
+
comment: Comment | None = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# -- list query responses ---------------------------------------------------
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class UsersResponse(ConnectionResponse[User]):
|
|
101
|
+
"""Response for [`users`][linear_python_client.client.LinearClient.users]."""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TeamsResponse(ConnectionResponse[Team]):
|
|
105
|
+
"""Response for [`teams`][linear_python_client.client.LinearClient.teams]."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class IssuesResponse(ConnectionResponse[Issue]):
|
|
109
|
+
"""Response for [`issues`][linear_python_client.client.LinearClient.issues]."""
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ProjectsResponse(ConnectionResponse[Project]):
|
|
113
|
+
"""Response for [`projects`][linear_python_client.client.LinearClient.projects]."""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CommentsResponse(ConnectionResponse[Comment]):
|
|
117
|
+
"""Response for [`comments`][linear_python_client.client.LinearClient.comments]."""
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class WorkflowStatesResponse(ConnectionResponse[WorkflowState]):
|
|
121
|
+
"""[`workflow_states`][linear_python_client.client.LinearClient.workflow_states] response."""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class IssueLabelsResponse(ConnectionResponse[IssueLabel]):
|
|
125
|
+
"""Response for [`issue_labels`][linear_python_client.client.LinearClient.issue_labels]."""
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# -- mutation responses -----------------------------------------------------
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class CreateIssueResponse(LinearModel):
|
|
132
|
+
"""Response for [`create_issue`][linear_python_client.client.LinearClient.create_issue]."""
|
|
133
|
+
|
|
134
|
+
success: bool = False
|
|
135
|
+
issue: Issue | None = None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class UpdateIssueResponse(LinearModel):
|
|
139
|
+
"""Response for [`update_issue`][linear_python_client.client.LinearClient.update_issue]."""
|
|
140
|
+
|
|
141
|
+
success: bool = False
|
|
142
|
+
issue: Issue | None = None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ArchiveIssueResponse(LinearModel):
|
|
146
|
+
"""Response for [`archive_issue`][linear_python_client.client.LinearClient.archive_issue]."""
|
|
147
|
+
|
|
148
|
+
success: bool = False
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class CreateCommentResponse(LinearModel):
|
|
152
|
+
"""Response for [`create_comment`][linear_python_client.client.LinearClient.create_comment]."""
|
|
153
|
+
|
|
154
|
+
success: bool = False
|
|
155
|
+
comment: Comment | None = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class AddLabelResponse(LinearModel):
|
|
159
|
+
"""Response for [`add_label`][linear_python_client.client.LinearClient.add_label]."""
|
|
160
|
+
|
|
161
|
+
success: bool = False
|
|
162
|
+
issue: Issue | None = None
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class RemoveLabelResponse(LinearModel):
|
|
166
|
+
"""Response for [`remove_label`][linear_python_client.client.LinearClient.remove_label]."""
|
|
167
|
+
|
|
168
|
+
success: bool = False
|
|
169
|
+
issue: Issue | None = None
|
|
File without changes
|