devrev-Python-SDK 1.0.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.
- devrev/__init__.py +47 -0
- devrev/client.py +343 -0
- devrev/config.py +180 -0
- devrev/exceptions.py +205 -0
- devrev/models/__init__.py +499 -0
- devrev/models/accounts.py +187 -0
- devrev/models/articles.py +109 -0
- devrev/models/base.py +147 -0
- devrev/models/code_changes.py +103 -0
- devrev/models/conversations.py +115 -0
- devrev/models/dev_users.py +258 -0
- devrev/models/groups.py +140 -0
- devrev/models/links.py +107 -0
- devrev/models/parts.py +110 -0
- devrev/models/rev_users.py +177 -0
- devrev/models/slas.py +112 -0
- devrev/models/tags.py +90 -0
- devrev/models/timeline_entries.py +100 -0
- devrev/models/webhooks.py +109 -0
- devrev/models/works.py +280 -0
- devrev/py.typed +1 -0
- devrev/services/__init__.py +74 -0
- devrev/services/accounts.py +325 -0
- devrev/services/articles.py +80 -0
- devrev/services/base.py +234 -0
- devrev/services/code_changes.py +80 -0
- devrev/services/conversations.py +98 -0
- devrev/services/dev_users.py +401 -0
- devrev/services/groups.py +103 -0
- devrev/services/links.py +68 -0
- devrev/services/parts.py +100 -0
- devrev/services/rev_users.py +235 -0
- devrev/services/slas.py +82 -0
- devrev/services/tags.py +80 -0
- devrev/services/timeline_entries.py +80 -0
- devrev/services/webhooks.py +80 -0
- devrev/services/works.py +363 -0
- devrev/utils/__init__.py +14 -0
- devrev/utils/deprecation.py +49 -0
- devrev/utils/http.py +521 -0
- devrev/utils/logging.py +139 -0
- devrev/utils/pagination.py +155 -0
- devrev_python_sdk-1.0.0.dist-info/METADATA +774 -0
- devrev_python_sdk-1.0.0.dist-info/RECORD +45 -0
- devrev_python_sdk-1.0.0.dist-info/WHEEL +4 -0
devrev/services/works.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""Works service for DevRev SDK.
|
|
2
|
+
|
|
3
|
+
This module provides the WorksService for managing DevRev work items (issues and tickets).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
from devrev.models.base import DateFilter, StageUpdate
|
|
13
|
+
from devrev.models.works import (
|
|
14
|
+
IssuePriority,
|
|
15
|
+
TicketSeverity,
|
|
16
|
+
Work,
|
|
17
|
+
WorksCountRequest,
|
|
18
|
+
WorksCountResponse,
|
|
19
|
+
WorksCreateRequest,
|
|
20
|
+
WorksCreateResponse,
|
|
21
|
+
WorksDeleteRequest,
|
|
22
|
+
WorksDeleteResponse,
|
|
23
|
+
WorksExportRequest,
|
|
24
|
+
WorksExportResponse,
|
|
25
|
+
WorksGetRequest,
|
|
26
|
+
WorksGetResponse,
|
|
27
|
+
WorksListRequest,
|
|
28
|
+
WorksListResponse,
|
|
29
|
+
WorksUpdateRequest,
|
|
30
|
+
WorksUpdateRequestOwnedBy,
|
|
31
|
+
WorksUpdateResponse,
|
|
32
|
+
WorkType,
|
|
33
|
+
)
|
|
34
|
+
from devrev.services.base import AsyncBaseService, BaseService
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from devrev.utils.http import AsyncHTTPClient, HTTPClient
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class WorksService(BaseService):
|
|
41
|
+
"""Synchronous service for managing DevRev work items.
|
|
42
|
+
|
|
43
|
+
Provides methods for creating, reading, updating, and deleting issues and tickets.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, http_client: HTTPClient) -> None:
|
|
47
|
+
"""Initialize the WorksService."""
|
|
48
|
+
super().__init__(http_client)
|
|
49
|
+
|
|
50
|
+
def create(
|
|
51
|
+
self,
|
|
52
|
+
title: str,
|
|
53
|
+
applies_to_part: str,
|
|
54
|
+
type: WorkType,
|
|
55
|
+
owned_by: Sequence[str],
|
|
56
|
+
*,
|
|
57
|
+
body: str | None = None,
|
|
58
|
+
priority: IssuePriority | None = None,
|
|
59
|
+
severity: TicketSeverity | None = None,
|
|
60
|
+
target_close_date: datetime | None = None,
|
|
61
|
+
custom_fields: dict[str, Any] | None = None,
|
|
62
|
+
) -> Work:
|
|
63
|
+
"""Create a new work item (issue or ticket).
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
title: Work item title
|
|
67
|
+
applies_to_part: Part ID this work applies to
|
|
68
|
+
type: Work type (issue or ticket)
|
|
69
|
+
owned_by: Owner user IDs (required)
|
|
70
|
+
body: Work item body/description
|
|
71
|
+
priority: Issue priority (for issues)
|
|
72
|
+
severity: Ticket severity (for tickets)
|
|
73
|
+
target_close_date: Target close date
|
|
74
|
+
custom_fields: Custom fields
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The created Work item
|
|
78
|
+
"""
|
|
79
|
+
request = WorksCreateRequest(
|
|
80
|
+
title=title,
|
|
81
|
+
applies_to_part=applies_to_part,
|
|
82
|
+
type=type,
|
|
83
|
+
owned_by=list(owned_by),
|
|
84
|
+
body=body,
|
|
85
|
+
priority=priority,
|
|
86
|
+
severity=severity,
|
|
87
|
+
target_close_date=target_close_date,
|
|
88
|
+
custom_fields=custom_fields,
|
|
89
|
+
)
|
|
90
|
+
response = self._post("/works.create", request, WorksCreateResponse)
|
|
91
|
+
return response.work
|
|
92
|
+
|
|
93
|
+
def get(self, id: str) -> Work:
|
|
94
|
+
"""Get a work item by ID.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
id: Work item ID
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The Work item
|
|
101
|
+
"""
|
|
102
|
+
request = WorksGetRequest(id=id)
|
|
103
|
+
response = self._post("/works.get", request, WorksGetResponse)
|
|
104
|
+
return response.work
|
|
105
|
+
|
|
106
|
+
def list(
|
|
107
|
+
self,
|
|
108
|
+
*,
|
|
109
|
+
type: Sequence[WorkType] | None = None,
|
|
110
|
+
applies_to_part: Sequence[str] | None = None,
|
|
111
|
+
created_by: Sequence[str] | None = None,
|
|
112
|
+
created_date: DateFilter | None = None,
|
|
113
|
+
cursor: str | None = None,
|
|
114
|
+
limit: int | None = None,
|
|
115
|
+
owned_by: Sequence[str] | None = None,
|
|
116
|
+
stage_name: Sequence[str] | None = None,
|
|
117
|
+
) -> WorksListResponse:
|
|
118
|
+
"""List work items.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
type: Filter by work types
|
|
122
|
+
applies_to_part: Filter by part IDs
|
|
123
|
+
created_by: Filter by creator user IDs
|
|
124
|
+
created_date: Filter by creation date
|
|
125
|
+
cursor: Pagination cursor
|
|
126
|
+
limit: Maximum number of results
|
|
127
|
+
owned_by: Filter by owner user IDs
|
|
128
|
+
stage_name: Filter by stage names
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Paginated list of work items
|
|
132
|
+
"""
|
|
133
|
+
request = WorksListRequest(
|
|
134
|
+
type=type,
|
|
135
|
+
applies_to_part=applies_to_part,
|
|
136
|
+
created_by=created_by,
|
|
137
|
+
created_date=created_date,
|
|
138
|
+
cursor=cursor,
|
|
139
|
+
limit=limit,
|
|
140
|
+
owned_by=owned_by,
|
|
141
|
+
stage_name=stage_name,
|
|
142
|
+
)
|
|
143
|
+
return self._post("/works.list", request, WorksListResponse)
|
|
144
|
+
|
|
145
|
+
def update(
|
|
146
|
+
self,
|
|
147
|
+
id: str,
|
|
148
|
+
*,
|
|
149
|
+
title: str | None = None,
|
|
150
|
+
body: str | None = None,
|
|
151
|
+
owned_by: Sequence[str] | None = None,
|
|
152
|
+
stage: StageUpdate | None = None,
|
|
153
|
+
priority: IssuePriority | None = None,
|
|
154
|
+
severity: TicketSeverity | None = None,
|
|
155
|
+
target_close_date: datetime | None = None,
|
|
156
|
+
) -> Work:
|
|
157
|
+
"""Update a work item.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
id: Work item ID
|
|
161
|
+
title: New title
|
|
162
|
+
body: New body/description
|
|
163
|
+
owned_by: New owner IDs
|
|
164
|
+
stage: New stage
|
|
165
|
+
priority: New priority (for issues)
|
|
166
|
+
severity: New severity (for tickets)
|
|
167
|
+
target_close_date: New target close date
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The updated Work item
|
|
171
|
+
"""
|
|
172
|
+
owned_by_update = WorksUpdateRequestOwnedBy(set=owned_by) if owned_by else None
|
|
173
|
+
request = WorksUpdateRequest(
|
|
174
|
+
id=id,
|
|
175
|
+
title=title,
|
|
176
|
+
body=body,
|
|
177
|
+
owned_by=owned_by_update,
|
|
178
|
+
stage=stage,
|
|
179
|
+
priority=priority,
|
|
180
|
+
severity=severity,
|
|
181
|
+
target_close_date=target_close_date,
|
|
182
|
+
)
|
|
183
|
+
response = self._post("/works.update", request, WorksUpdateResponse)
|
|
184
|
+
return response.work
|
|
185
|
+
|
|
186
|
+
def delete(self, id: str) -> None:
|
|
187
|
+
"""Delete a work item.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
id: Work item ID to delete
|
|
191
|
+
"""
|
|
192
|
+
request = WorksDeleteRequest(id=id)
|
|
193
|
+
self._post("/works.delete", request, WorksDeleteResponse)
|
|
194
|
+
|
|
195
|
+
def export(
|
|
196
|
+
self,
|
|
197
|
+
*,
|
|
198
|
+
type: Sequence[WorkType] | None = None,
|
|
199
|
+
applies_to_part: Sequence[str] | None = None,
|
|
200
|
+
created_by: Sequence[str] | None = None,
|
|
201
|
+
created_date: DateFilter | None = None,
|
|
202
|
+
first: int | None = None,
|
|
203
|
+
) -> Sequence[Work]:
|
|
204
|
+
"""Export work items.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
type: Filter by work types
|
|
208
|
+
applies_to_part: Filter by part IDs
|
|
209
|
+
created_by: Filter by creator user IDs
|
|
210
|
+
created_date: Filter by creation date
|
|
211
|
+
first: Maximum number of results
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
List of exported work items
|
|
215
|
+
"""
|
|
216
|
+
request = WorksExportRequest(
|
|
217
|
+
type=type,
|
|
218
|
+
applies_to_part=applies_to_part,
|
|
219
|
+
created_by=created_by,
|
|
220
|
+
created_date=created_date,
|
|
221
|
+
first=first,
|
|
222
|
+
)
|
|
223
|
+
response = self._post("/works.export", request, WorksExportResponse)
|
|
224
|
+
return response.works
|
|
225
|
+
|
|
226
|
+
def count(
|
|
227
|
+
self,
|
|
228
|
+
*,
|
|
229
|
+
type: Sequence[WorkType] | None = None,
|
|
230
|
+
applies_to_part: Sequence[str] | None = None,
|
|
231
|
+
created_by: Sequence[str] | None = None,
|
|
232
|
+
owned_by: Sequence[str] | None = None,
|
|
233
|
+
) -> int:
|
|
234
|
+
"""Count work items.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
type: Filter by work types
|
|
238
|
+
applies_to_part: Filter by part IDs
|
|
239
|
+
created_by: Filter by creator user IDs
|
|
240
|
+
owned_by: Filter by owner user IDs
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Number of matching work items
|
|
244
|
+
"""
|
|
245
|
+
request = WorksCountRequest(
|
|
246
|
+
type=type,
|
|
247
|
+
applies_to_part=applies_to_part,
|
|
248
|
+
created_by=created_by,
|
|
249
|
+
owned_by=owned_by,
|
|
250
|
+
)
|
|
251
|
+
response = self._post("/works.count", request, WorksCountResponse)
|
|
252
|
+
return response.count
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class AsyncWorksService(AsyncBaseService):
|
|
256
|
+
"""Asynchronous service for managing DevRev work items."""
|
|
257
|
+
|
|
258
|
+
def __init__(self, http_client: AsyncHTTPClient) -> None:
|
|
259
|
+
"""Initialize the AsyncWorksService."""
|
|
260
|
+
super().__init__(http_client)
|
|
261
|
+
|
|
262
|
+
async def create(
|
|
263
|
+
self,
|
|
264
|
+
title: str,
|
|
265
|
+
applies_to_part: str,
|
|
266
|
+
type: WorkType,
|
|
267
|
+
owned_by: Sequence[str],
|
|
268
|
+
*,
|
|
269
|
+
body: str | None = None,
|
|
270
|
+
priority: IssuePriority | None = None,
|
|
271
|
+
severity: TicketSeverity | None = None,
|
|
272
|
+
target_close_date: datetime | None = None,
|
|
273
|
+
custom_fields: dict[str, Any] | None = None,
|
|
274
|
+
) -> Work:
|
|
275
|
+
"""Create a new work item."""
|
|
276
|
+
request = WorksCreateRequest(
|
|
277
|
+
title=title,
|
|
278
|
+
applies_to_part=applies_to_part,
|
|
279
|
+
type=type,
|
|
280
|
+
owned_by=list(owned_by),
|
|
281
|
+
body=body,
|
|
282
|
+
priority=priority,
|
|
283
|
+
severity=severity,
|
|
284
|
+
target_close_date=target_close_date,
|
|
285
|
+
custom_fields=custom_fields,
|
|
286
|
+
)
|
|
287
|
+
response = await self._post("/works.create", request, WorksCreateResponse)
|
|
288
|
+
return response.work
|
|
289
|
+
|
|
290
|
+
async def get(self, id: str) -> Work:
|
|
291
|
+
"""Get a work item by ID."""
|
|
292
|
+
request = WorksGetRequest(id=id)
|
|
293
|
+
response = await self._post("/works.get", request, WorksGetResponse)
|
|
294
|
+
return response.work
|
|
295
|
+
|
|
296
|
+
async def list(
|
|
297
|
+
self,
|
|
298
|
+
*,
|
|
299
|
+
type: Sequence[WorkType] | None = None,
|
|
300
|
+
applies_to_part: Sequence[str] | None = None,
|
|
301
|
+
cursor: str | None = None,
|
|
302
|
+
limit: int | None = None,
|
|
303
|
+
owned_by: Sequence[str] | None = None,
|
|
304
|
+
) -> WorksListResponse:
|
|
305
|
+
"""List work items."""
|
|
306
|
+
request = WorksListRequest(
|
|
307
|
+
type=type,
|
|
308
|
+
applies_to_part=applies_to_part,
|
|
309
|
+
cursor=cursor,
|
|
310
|
+
limit=limit,
|
|
311
|
+
owned_by=owned_by,
|
|
312
|
+
)
|
|
313
|
+
return await self._post("/works.list", request, WorksListResponse)
|
|
314
|
+
|
|
315
|
+
async def update(
|
|
316
|
+
self,
|
|
317
|
+
id: str,
|
|
318
|
+
*,
|
|
319
|
+
title: str | None = None,
|
|
320
|
+
body: str | None = None,
|
|
321
|
+
owned_by: Sequence[str] | None = None,
|
|
322
|
+
priority: IssuePriority | None = None,
|
|
323
|
+
severity: TicketSeverity | None = None,
|
|
324
|
+
) -> Work:
|
|
325
|
+
"""Update a work item."""
|
|
326
|
+
owned_by_update = WorksUpdateRequestOwnedBy(set=owned_by) if owned_by else None
|
|
327
|
+
request = WorksUpdateRequest(
|
|
328
|
+
id=id,
|
|
329
|
+
title=title,
|
|
330
|
+
body=body,
|
|
331
|
+
owned_by=owned_by_update,
|
|
332
|
+
priority=priority,
|
|
333
|
+
severity=severity,
|
|
334
|
+
)
|
|
335
|
+
response = await self._post("/works.update", request, WorksUpdateResponse)
|
|
336
|
+
return response.work
|
|
337
|
+
|
|
338
|
+
async def delete(self, id: str) -> None:
|
|
339
|
+
"""Delete a work item."""
|
|
340
|
+
request = WorksDeleteRequest(id=id)
|
|
341
|
+
await self._post("/works.delete", request, WorksDeleteResponse)
|
|
342
|
+
|
|
343
|
+
async def export(
|
|
344
|
+
self,
|
|
345
|
+
*,
|
|
346
|
+
type: Sequence[WorkType] | None = None,
|
|
347
|
+
first: int | None = None,
|
|
348
|
+
) -> Sequence[Work]:
|
|
349
|
+
"""Export work items."""
|
|
350
|
+
request = WorksExportRequest(type=type, first=first)
|
|
351
|
+
response = await self._post("/works.export", request, WorksExportResponse)
|
|
352
|
+
return response.works
|
|
353
|
+
|
|
354
|
+
async def count(
|
|
355
|
+
self,
|
|
356
|
+
*,
|
|
357
|
+
type: Sequence[WorkType] | None = None,
|
|
358
|
+
owned_by: Sequence[str] | None = None,
|
|
359
|
+
) -> int:
|
|
360
|
+
"""Count work items."""
|
|
361
|
+
request = WorksCountRequest(type=type, owned_by=owned_by)
|
|
362
|
+
response = await self._post("/works.count", request, WorksCountResponse)
|
|
363
|
+
return response.count
|
devrev/utils/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""DevRev SDK Utilities.
|
|
2
|
+
|
|
3
|
+
This module contains utility functions and classes used throughout the SDK.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from devrev.utils.deprecation import deprecated
|
|
7
|
+
from devrev.utils.logging import ColoredFormatter, configure_logging, get_logger
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ColoredFormatter",
|
|
11
|
+
"configure_logging",
|
|
12
|
+
"deprecated",
|
|
13
|
+
"get_logger",
|
|
14
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Deprecation utilities.
|
|
2
|
+
|
|
3
|
+
This module provides a small, explicit deprecation mechanism for the public SDK.
|
|
4
|
+
|
|
5
|
+
See: DEPRECATIONS.md
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from functools import wraps
|
|
12
|
+
from typing import ParamSpec, TypeVar
|
|
13
|
+
from warnings import warn
|
|
14
|
+
|
|
15
|
+
P = ParamSpec("P")
|
|
16
|
+
R = TypeVar("R")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def deprecated(
|
|
20
|
+
version: str,
|
|
21
|
+
reason: str,
|
|
22
|
+
replacement: str | None = None,
|
|
23
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
24
|
+
"""Mark a callable as deprecated.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
version: Version the symbol was deprecated in (e.g. "1.2.0").
|
|
28
|
+
reason: Short explanation of why it is deprecated.
|
|
29
|
+
replacement: Optional replacement symbol or guidance.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
A decorator that emits a DeprecationWarning on each call.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
36
|
+
@wraps(func)
|
|
37
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
38
|
+
msg = f"{func.__name__} is deprecated since {version}: {reason}"
|
|
39
|
+
if replacement:
|
|
40
|
+
msg += f". Use {replacement} instead."
|
|
41
|
+
warn(msg, DeprecationWarning, stacklevel=2)
|
|
42
|
+
return func(*args, **kwargs)
|
|
43
|
+
|
|
44
|
+
return wrapper
|
|
45
|
+
|
|
46
|
+
return decorator
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__all__ = ["deprecated"]
|