python-backlog 0.6.0__py3-none-any.whl → 0.7.2__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.
backlog/base.py CHANGED
@@ -9,6 +9,7 @@ from backlog.issue import Issue
9
9
  from backlog.git import Git
10
10
  from backlog.user import User
11
11
  from backlog.category import Category
12
+ from backlog.custom_field import CustomField
12
13
 
13
14
  BACKLOG_BASE_URL = "https://{space}.backlog.jp"
14
15
  BACKLOG_URI_PREFIX = "/api/v2/"
@@ -97,6 +98,7 @@ class BacklogAPI(BaseAPI):
97
98
  self.git = Git(self)
98
99
  self.user = User(self)
99
100
  self.category = Category(self)
101
+ self.custom_field = CustomField(self)
100
102
 
101
103
  def get(self, method, uri, query_param, request_param, headers, **kwargs):
102
104
  _url = self.base_url + uri
@@ -0,0 +1,378 @@
1
+ from enum import Enum
2
+ from typing import List, TypedDict, NewType, Optional, Union
3
+
4
+
5
+ class CustomFieldResponse(TypedDict):
6
+ id: int
7
+ projectId: int
8
+ typeId: int
9
+ version: str
10
+ name: str
11
+ description: str
12
+ required: bool
13
+ useIssueType: bool
14
+ applicableIssueTypes: List[int]
15
+ displayOrder: int
16
+
17
+
18
+ class _InitialDate(TypedDict):
19
+ id: int
20
+ shift: Optional[int]
21
+ date: Optional[str]
22
+
23
+
24
+ class NumberCustomFieldResponse(CustomFieldResponse):
25
+ min: int
26
+ max: int
27
+ initialValue: int
28
+ unit: str
29
+ initialDate: _InitialDate
30
+
31
+
32
+ class DateCustomFieldResponse(CustomFieldResponse):
33
+ min: str
34
+ max: str
35
+ initialValueType: int
36
+ initialDate: str
37
+ initialShift: int
38
+
39
+
40
+ class _ListCustomFieldResponseItem(TypedDict):
41
+ id: int
42
+ name: str
43
+ displayOrder: int
44
+
45
+
46
+ class ListCustomFieldResponse(CustomFieldResponse):
47
+ items: List[_ListCustomFieldResponseItem]
48
+ allowInput: bool
49
+ allowAddItem: bool
50
+
51
+
52
+ AddTextCustomFieldResponse = NewType("AddTextCustomFieldResponse", CustomFieldResponse)
53
+ AddSentenceCustomFieldResponse = NewType("AddSentenceCustomFieldResponse", CustomFieldResponse)
54
+ AddNumberCustomFieldResponse = NewType("AddNumberCustomFieldResponse", NumberCustomFieldResponse)
55
+ AddDateCustomFieldResponse = NewType("AddDateCustomFieldResponse", DateCustomFieldResponse)
56
+ AddSingleListCustomFieldResponse = NewType("AddSingleListCustomFieldResponse", ListCustomFieldResponse)
57
+ AddMultipleListCustomFieldResponse = NewType("AddMultipleListCustomFieldResponse", ListCustomFieldResponse)
58
+ AddCheckboxCustomFieldResponse = NewType("AddCheckboxCustomFieldResponse", ListCustomFieldResponse)
59
+ AddRadioCustomFieldResponse = NewType("AddRadioCustomFieldResponse", ListCustomFieldResponse)
60
+ AddCustomFiledResponse = Union[
61
+ AddTextCustomFieldResponse, AddSentenceCustomFieldResponse,
62
+ AddNumberCustomFieldResponse, AddDateCustomFieldResponse,
63
+ AddSingleListCustomFieldResponse, AddMultipleListCustomFieldResponse,
64
+ AddCheckboxCustomFieldResponse, AddRadioCustomFieldResponse,
65
+ ]
66
+
67
+ DeleteTextCustomFieldResponse = NewType("DeleteTextCustomFieldResponse", CustomFieldResponse)
68
+ DeleteSentenceCustomFieldResponse = NewType("DeleteSentenceCustomFieldResponse", CustomFieldResponse)
69
+ DeleteNumberCustomFieldResponse = NewType("DeleteNumberCustomFieldResponse", NumberCustomFieldResponse)
70
+ DeleteDateCustomFieldResponse = NewType("DeleteDateCustomFieldResponse", DateCustomFieldResponse)
71
+ DeleteSingleListCustomFieldResponse = NewType("DeleteSingleListCustomFieldResponse", ListCustomFieldResponse)
72
+ DeleteMultipleListCustomFieldResponse = NewType("DeleteMultipleListCustomFieldResponse", ListCustomFieldResponse)
73
+ DeleteCheckboxCustomFieldResponse = NewType("DeleteCheckboxCustomFieldResponse", ListCustomFieldResponse)
74
+ DeleteRadioCustomFieldResponse = NewType("DeleteRadioCustomFieldResponse", ListCustomFieldResponse)
75
+ DeleteCustomFiledResponse = Union[
76
+ DeleteTextCustomFieldResponse, DeleteSentenceCustomFieldResponse,
77
+ DeleteNumberCustomFieldResponse, DeleteDateCustomFieldResponse,
78
+ DeleteSingleListCustomFieldResponse, DeleteMultipleListCustomFieldResponse,
79
+ DeleteCheckboxCustomFieldResponse, DeleteRadioCustomFieldResponse,
80
+ ]
81
+
82
+
83
+ class CustomField(object):
84
+ class TypeId(Enum):
85
+ TEXT = 1
86
+ SENTENCE = 2
87
+ NUMBER = 3
88
+ DATE = 4
89
+ SINGLE_LIST = 5
90
+ MULTIPLE_LIST = 6
91
+ CHECKBOX = 7
92
+ RADIO = 8
93
+
94
+ class DateInitialValueType(Enum):
95
+ TODAY = 1
96
+ TODAY_PLUS_INITIAL_SHIFT = 2
97
+ SPECIFIED_DAY = 3
98
+
99
+ def __init__(self, api) -> None:
100
+ self.api = api
101
+
102
+ def list(self, projectIdOrKey: str):
103
+ """List custom fields
104
+ https://developer.nulab.com/docs/backlog/api/2/get-custom-field-list/
105
+
106
+ :param projectIdOrKey: project id or key
107
+ :type projectIdOrKey: str
108
+ """
109
+ _url = f"projects/{projectIdOrKey}/customFields"
110
+ _method = "GET"
111
+
112
+ resp = self.api.invoke_method(_method, _url)
113
+
114
+ return resp.json()
115
+
116
+ def add(
117
+ self,
118
+ projectIdOrKey: str,
119
+ typeId: Union[TypeId, int],
120
+ name: str,
121
+ applicableIssueTypes: Optional[List[int]] = None,
122
+ description: Optional[str] = None,
123
+ required: Optional[bool] = None,
124
+ **kwargs,
125
+ ) -> AddCustomFiledResponse:
126
+ """Add custom field
127
+ https://developer.nulab.com/docs/backlog/api/2/add-custom-field/#
128
+ """
129
+ _url = f"projects/{projectIdOrKey}/customFields"
130
+ _method = "POST"
131
+ tid = typeId
132
+ if isinstance(typeId, Enum):
133
+ tid = typeId.value
134
+ elif isinstance(typeId, int):
135
+ tid = typeId
136
+ else:
137
+ raise ValueError(f"invalid typeId: {typeId}")
138
+ request_param = {
139
+ "typeId": tid,
140
+ "name": name,
141
+ "applicableIssueTypes": applicableIssueTypes,
142
+ "description": description,
143
+ "required": required,
144
+ **kwargs,
145
+ }
146
+ for k, v in request_param.copy().items():
147
+ if v is None:
148
+ del request_param[k]
149
+ if type(v) is bool:
150
+ request_param[k] = "true" if v else "false"
151
+
152
+ # for backlog parameter key spec
153
+ if "applicableIssueTypes" in request_param:
154
+ request_param["applicableIssueTypes[]"] = request_param["applicableIssueTypes"]
155
+ del request_param["applicableIssueTypes"]
156
+ if "items" in request_param:
157
+ request_param["items[]"] = request_param["items"]
158
+ del request_param["items"]
159
+
160
+ resp = self.api.invoke_method(_method, _url, request_param=request_param)
161
+
162
+ return resp.json()
163
+
164
+ def add_text_custom_field(
165
+ self,
166
+ projectIdOrKey: str,
167
+ name: str,
168
+ applicableIssueTypes: Optional[List[int]] = None,
169
+ description: Optional[str] = None,
170
+ required: Optional[bool] = None,
171
+ ) -> AddTextCustomFieldResponse:
172
+ return self.add(
173
+ projectIdOrKey,
174
+ 1,
175
+ name,
176
+ applicableIssueTypes,
177
+ description,
178
+ required,
179
+ )
180
+
181
+ def add_sentence_custom_field(
182
+ self,
183
+ projectIdOrKey: str,
184
+ name: str,
185
+ applicableIssueTypes: Optional[List[int]] = None,
186
+ description: Optional[str] = None,
187
+ required: Optional[bool] = None,
188
+ ) -> AddSentenceCustomFieldResponse:
189
+ return self.add(
190
+ projectIdOrKey,
191
+ 2,
192
+ name,
193
+ applicableIssueTypes,
194
+ description,
195
+ required,
196
+ )
197
+
198
+ def add_number_custom_field(
199
+ self,
200
+ projectIdOrKey: str,
201
+ name: str,
202
+ applicableIssueTypes: Optional[List[int]] = None,
203
+ description: Optional[str] = None,
204
+ required: Optional[bool] = None,
205
+ min: Optional[int] = None,
206
+ max: Optional[int] = None,
207
+ initialValue: Optional[int] = None,
208
+ unit: Optional[str] = None,
209
+ ) -> AddNumberCustomFieldResponse:
210
+ return self.add(
211
+ projectIdOrKey,
212
+ 3,
213
+ name,
214
+ applicableIssueTypes,
215
+ description,
216
+ required,
217
+ min=min,
218
+ max=max,
219
+ initialValue=initialValue,
220
+ unit=unit,
221
+ )
222
+
223
+ def add_date_custom_field(
224
+ self,
225
+ projectIdOrKey: str,
226
+ name: str,
227
+ applicableIssueTypes: Optional[List[int]] = None,
228
+ description: Optional[str] = None,
229
+ required: Optional[bool] = None,
230
+ min: Optional[str] = None, # yyyy-MM-dd
231
+ max: Optional[str] = None, # yyyy-MM-dd
232
+ initialValueType: Optional[DateInitialValueType] = None,
233
+ initialDate: Optional[str] = None, # yyyy-MM-dd
234
+ initialShift: Optional[int] = None,
235
+ ) -> AddDateCustomFieldResponse:
236
+ if isinstance(initialValueType, Enum):
237
+ initialValueType = initialValueType.value
238
+
239
+ if (
240
+ (initialValueType == self.DateInitialValueType.SPECIFIED_DAY.value)
241
+ and (initialDate is None)
242
+ ):
243
+ raise ValueError("initialDate is required when initialValueType is SPECIFIED_DAY")
244
+
245
+ return self.add(
246
+ projectIdOrKey,
247
+ 4,
248
+ name,
249
+ applicableIssueTypes,
250
+ description,
251
+ required,
252
+ min=min,
253
+ max=max,
254
+ initialValueType=initialValueType,
255
+ initialDate=initialDate,
256
+ initialShift=initialShift,
257
+ )
258
+
259
+ def add_single_list_custom_field(
260
+ self,
261
+ projectIdOrKey: str,
262
+ name: str,
263
+ applicableIssueTypes: Optional[List[int]] = None,
264
+ description: Optional[str] = None,
265
+ required: Optional[bool] = None,
266
+ items: Optional[List[str]] = None,
267
+ allowInput: Optional[bool] = None,
268
+ allowAddItem: Optional[bool] = None,
269
+ ) -> AddSingleListCustomFieldResponse:
270
+ return self.add(
271
+ projectIdOrKey,
272
+ 5,
273
+ name,
274
+ applicableIssueTypes,
275
+ description,
276
+ required,
277
+ items=items,
278
+ allowInput=allowInput,
279
+ allowAddItem=allowAddItem,
280
+ )
281
+
282
+ def add_multiple_list_custom_field(
283
+ self,
284
+ projectIdOrKey: str,
285
+ name: str,
286
+ applicableIssueTypes: Optional[List[int]] = None,
287
+ description: Optional[str] = None,
288
+ required: Optional[bool] = None,
289
+ items: Optional[List[str]] = None,
290
+ allowInput: Optional[bool] = None,
291
+ allowAddItem: Optional[bool] = None,
292
+ ) -> AddMultipleListCustomFieldResponse:
293
+ return self.add(
294
+ projectIdOrKey,
295
+ 6,
296
+ name,
297
+ applicableIssueTypes,
298
+ description,
299
+ required,
300
+ items=items,
301
+ allowInput=allowInput,
302
+ allowAddItem=allowAddItem,
303
+ )
304
+
305
+ def add_checkbox_custom_field(
306
+ self,
307
+ projectIdOrKey: str,
308
+ name: str,
309
+ applicableIssueTypes: Optional[List[int]] = None,
310
+ description: Optional[str] = None,
311
+ required: Optional[bool] = None,
312
+ items: Optional[List[str]] = None,
313
+ allowInput: Optional[bool] = None,
314
+ allowAddItem: Optional[bool] = None,
315
+ ) -> AddCheckboxCustomFieldResponse:
316
+ return self.add(
317
+ projectIdOrKey,
318
+ 7,
319
+ name,
320
+ applicableIssueTypes,
321
+ description,
322
+ required,
323
+ items=items,
324
+ allowInput=allowInput,
325
+ allowAddItem=allowAddItem,
326
+ )
327
+
328
+ def add_radio_custom_field(
329
+ self,
330
+ projectIdOrKey: str,
331
+ name: str,
332
+ applicableIssueTypes: Optional[List[int]] = None,
333
+ description: Optional[str] = None,
334
+ required: Optional[bool] = None,
335
+ items: Optional[List[str]] = None,
336
+ allowInput: Optional[bool] = None,
337
+ allowAddItem: Optional[bool] = None,
338
+ ) -> AddRadioCustomFieldResponse:
339
+ return self.add(
340
+ projectIdOrKey,
341
+ 8,
342
+ name,
343
+ applicableIssueTypes,
344
+ description,
345
+ required,
346
+ items=items,
347
+ allowInput=allowInput,
348
+ allowAddItem=allowAddItem,
349
+ )
350
+
351
+ # def update(self, projectIdOrKey: str, id: int, name: str):
352
+ # """Update category
353
+ # https://developer.nulab.com/docs/backlog/api/2/update-category/#
354
+
355
+ # :param projectIdOrKey: project id or key
356
+ # :type projectIdOrKey: str
357
+ # :param id: category id
358
+ # :type id: int
359
+ # :param name: category name
360
+ # :type name: str
361
+ # """
362
+ # _url = f"projects/{projectIdOrKey}/categories/{id}"
363
+ # _method = "PATCH"
364
+
365
+ # resp = self.api.invoke_method(_method, _url, request_param={"name": name})
366
+
367
+ # return resp.json()
368
+
369
+ def delete(self, projectIdOrKey: str, id: int) -> DeleteCustomFiledResponse:
370
+ """Delete custom field
371
+ https://developer.nulab.com/docs/backlog/api/2/delete-custom-field/#delete-custom-field
372
+ """
373
+ _url = f"projects/{projectIdOrKey}/customFields/{id}"
374
+ _method = "DELETE"
375
+
376
+ resp = self.api.invoke_method(_method, _url)
377
+
378
+ return resp.json()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-backlog
3
- Version: 0.6.0
3
+ Version: 0.7.2
4
4
  Summary: Backlog API v2 wrapper
5
5
  Home-page: https://github.com/hassaku63/pbl
6
6
  Author: hassaku63
@@ -8,7 +8,6 @@ Author-email: hassaku63@gmail.com
8
8
  License: MIT
9
9
  Project-URL: Source, https://github.com/hassaku63/pbl
10
10
  Keywords: nulab backlog
11
- Platform: UNKNOWN
12
11
  Classifier: Development Status :: 4 - Beta
13
12
  Classifier: Programming Language :: Python
14
13
  Classifier: Programming Language :: Python :: 3.8
@@ -18,7 +17,8 @@ Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Natural Language :: Japanese
19
18
  Requires-Python: >=3.8
20
19
  Description-Content-Type: text/markdown
21
- Requires-Dist: requests ~=2.31.0
20
+ License-File: LICENSE
21
+ Requires-Dist: requests ~=2.32.2
22
22
 
23
23
  # pbl
24
24
 
@@ -87,5 +87,3 @@ Qiita https://qiita.com/hassaku_63/items/b9eb2a1c7ecd3c19507d
87
87
  ## Contact
88
88
 
89
89
  Twitter: [hassaku_63](https://twitter.com/hassaku_63)
90
-
91
-
@@ -1,14 +1,15 @@
1
1
  backlog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  backlog/attachment.py,sha256=RpaAcTnr_2CsJUDeeJHT1YSH0TwPImH5C0uwUOpDlHs,764
3
- backlog/base.py,sha256=m5sArzWDNL5ZBd8HtKZMPg-PW8DXHqfqWOyEm7XCNq8,5541
3
+ backlog/base.py,sha256=-EUjAZr20vq3i8ekPe9LwbGvwJZ6PGhGCV8TsqZqIrs,5632
4
4
  backlog/category.py,sha256=DQ06yhOCXxtDbPMZ1Ig1QD8n7Ht_ZFrp8PJzO18IeZk,2530
5
+ backlog/custom_field.py,sha256=dJ-XnrYv0rlpLvPhIkZEgcyNjZB8GR3352Or8HUPKlc,12243
5
6
  backlog/git.py,sha256=mazfvwn1Ekga0eeigk22Xf3vhXEo-P5Irfyq6r_QbaA,3056
6
7
  backlog/issue.py,sha256=of0DQ_1fsBPJ2aU6TS7IWlw0DzZIJRzo4fCGuKv2JY8,6636
7
8
  backlog/project.py,sha256=TOBDLV6eSpeQRxcjZ1ArrEE8lHRE7JIyaCJUQALSdSg,13839
8
9
  backlog/user.py,sha256=fgd1X1fom1mTPKDU6DFqBspPauq6UuhxrbFd9CCrptU,2829
9
10
  backlog/wiki.py,sha256=NjMkHopNb-9oic1yziiypzzpIHSdXcRfePZhOBKtDok,5431
10
- python_backlog-0.6.0.dist-info/LICENSE,sha256=1teWq4411yGkUz_9lD1V74mcHFRix2IqOAoFaghKYwo,1073
11
- python_backlog-0.6.0.dist-info/METADATA,sha256=NEFTIvy8HT4XYHbpYbiCctdWt6qDXtB7yDMicdjLRVI,2164
12
- python_backlog-0.6.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
13
- python_backlog-0.6.0.dist-info/top_level.txt,sha256=qzHRU2P6F24ntkXGiIBl4Qf_AlV0BRebSc5tuLlcOIE,8
14
- python_backlog-0.6.0.dist-info/RECORD,,
11
+ python_backlog-0.7.2.dist-info/LICENSE,sha256=1teWq4411yGkUz_9lD1V74mcHFRix2IqOAoFaghKYwo,1073
12
+ python_backlog-0.7.2.dist-info/METADATA,sha256=t_HkpWuFHIQUfbOrhIvWjkPGkq5lWsX4Y4PPb4EqcK8,2166
13
+ python_backlog-0.7.2.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
14
+ python_backlog-0.7.2.dist-info/top_level.txt,sha256=qzHRU2P6F24ntkXGiIBl4Qf_AlV0BRebSc5tuLlcOIE,8
15
+ python_backlog-0.7.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: setuptools (74.1.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5