chub-dev 0.1.0 → 0.1.2-beta.0

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.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1492 @@
1
+ ---
2
+ name: issues
3
+ description: "Jira Python SDK Coding Guidelines for writing code using the Jira API with official libraries and SDKs"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "3.10.5"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "jira,issues,atlassian,project-management,agile"
10
+ ---
11
+
12
+ # Jira Python SDK Coding Guidelines
13
+
14
+ You are a Jira API coding expert. Help me with writing code using the Jira API calling the official libraries and SDKs.
15
+
16
+ You can find the official Jira API documentation and code samples here:
17
+ https://jira.readthedocs.io/
18
+ https://developer.atlassian.com/cloud/jira/platform/rest/v3/
19
+
20
+ ## Golden Rule: Use the Correct and Current SDK
21
+
22
+ Always use the jira Python library to interact with the Jira Cloud, Jira Server, and Jira Data Center REST APIs. This is the most actively maintained and comprehensive Python wrapper for Jira APIs.
23
+
24
+ - **Library Name:** jira
25
+ - **PyPI Package:** `jira`
26
+ - **Minimum Python Version:** 3.10 or newer
27
+ - **Legacy Libraries**: `jira-python`, `atlassian-python-api` (different library), and other unofficial packages may have different APIs
28
+
29
+ **Installation:**
30
+
31
+ - **Correct:** `pip install jira`
32
+ - **Correct:** `pip install jira[opt,cli,test]` (with optional dependencies)
33
+
34
+ **APIs and Usage:**
35
+
36
+ - **Correct:** `from jira import JIRA`
37
+ - **Correct:** `jira = JIRA('https://your-domain.atlassian.net', basic_auth=('email', 'token'))`
38
+ - **Correct:** `jira.search_issues('project = PROJ')`
39
+ - **Incorrect:** Using REST API endpoints directly without the SDK
40
+ - **Incorrect:** Using deprecated cookie-based authentication
41
+ - **Incorrect:** Using username/password for Jira Cloud (use API tokens instead)
42
+
43
+ ## Authentication
44
+
45
+ The jira library supports multiple authentication methods. Choose the appropriate method based on your Jira instance type.
46
+
47
+ ### API Token Authentication (Recommended for Jira Cloud)
48
+
49
+ Generate an API token at: https://id.atlassian.com/manage-profile/security/api-tokens
50
+
51
+ ```python
52
+ from jira import JIRA
53
+
54
+ # Using basic_auth with email and API token
55
+ jira = JIRA(
56
+ server='https://your-domain.atlassian.net',
57
+ basic_auth=('your.email@example.com', 'your_api_token')
58
+ )
59
+ ```
60
+
61
+ ### Personal Access Token (PAT) Authentication (Self-Hosted Jira)
62
+
63
+ For Jira Server/Data Center instances:
64
+
65
+ ```python
66
+ from jira import JIRA
67
+
68
+ # Using token_auth parameter
69
+ jira = JIRA(
70
+ server='https://jira.yourcompany.com',
71
+ token_auth='your_personal_access_token'
72
+ )
73
+
74
+ # Alternative: Using headers
75
+ headers = JIRA.DEFAULT_OPTIONS["headers"].copy()
76
+ headers["Authorization"] = f"Bearer {personal_access_token}"
77
+
78
+ jira = JIRA(
79
+ server='https://jira.yourcompany.com',
80
+ options={"headers": headers}
81
+ )
82
+ ```
83
+
84
+ ### OAuth Authentication
85
+
86
+ For OAuth integrations:
87
+
88
+ ```python
89
+ from jira import JIRA
90
+
91
+ oauth_dict = {
92
+ 'access_token': 'your_access_token',
93
+ 'access_token_secret': 'your_access_token_secret',
94
+ 'consumer_key': 'your_consumer_key',
95
+ 'key_cert': key_cert_data # RSA private key data
96
+ }
97
+
98
+ jira = JIRA(
99
+ server='https://your-domain.atlassian.net',
100
+ oauth=oauth_dict
101
+ )
102
+ ```
103
+
104
+ ### Kerberos Authentication
105
+
106
+ For enterprise environments with Kerberos:
107
+
108
+ ```python
109
+ from jira import JIRA
110
+
111
+ # Basic Kerberos
112
+ jira = JIRA(server='https://jira.yourcompany.com', kerberos=True)
113
+
114
+ # With options
115
+ jira = JIRA(
116
+ server='https://jira.yourcompany.com',
117
+ kerberos=True,
118
+ kerberos_options={'mutual_authentication': 'DISABLED'}
119
+ )
120
+ ```
121
+
122
+ ### Environment Variables Setup
123
+
124
+ Create a `.env` file or set environment variables:
125
+
126
+ ```bash
127
+ JIRA_SERVER=https://your-domain.atlassian.net
128
+ JIRA_EMAIL=your.email@example.com
129
+ JIRA_API_TOKEN=your_api_token_here
130
+ ```
131
+
132
+ Load environment variables in your code:
133
+
134
+ ```python
135
+ import os
136
+ from jira import JIRA
137
+
138
+ jira = JIRA(
139
+ server=os.environ['JIRA_SERVER'],
140
+ basic_auth=(os.environ['JIRA_EMAIL'], os.environ['JIRA_API_TOKEN'])
141
+ )
142
+ ```
143
+
144
+ ## Initialization
145
+
146
+ Create a JIRA client instance for all API interactions:
147
+
148
+ ```python
149
+ from jira import JIRA
150
+
151
+ # Basic initialization
152
+ jira = JIRA(
153
+ server='https://your-domain.atlassian.net',
154
+ basic_auth=('your.email@example.com', 'your_api_token')
155
+ )
156
+
157
+ # With custom options
158
+ options = {
159
+ 'server': 'https://your-domain.atlassian.net',
160
+ 'verify': True, # SSL certificate verification
161
+ 'max_retries': 3,
162
+ 'timeout': 30,
163
+ }
164
+
165
+ jira = JIRA(
166
+ options=options,
167
+ basic_auth=('your.email@example.com', 'your_api_token')
168
+ )
169
+ ```
170
+
171
+ ## Error Handling
172
+
173
+ Handle exceptions for robust applications:
174
+
175
+ ```python
176
+ from jira import JIRA, JIRAError
177
+
178
+ jira = JIRA(
179
+ server='https://your-domain.atlassian.net',
180
+ basic_auth=('your.email@example.com', 'your_api_token')
181
+ )
182
+
183
+ try:
184
+ issue = jira.issue('PROJ-123')
185
+ print(f'Issue: {issue.key} - {issue.fields.summary}')
186
+ except JIRAError as e:
187
+ if e.status_code == 404:
188
+ print('Issue not found')
189
+ elif e.status_code == 401:
190
+ print('Authentication failed')
191
+ elif e.status_code == 403:
192
+ print('Permission denied')
193
+ else:
194
+ print(f'Error: {e.status_code} - {e.text}')
195
+ except Exception as e:
196
+ print(f'Unexpected error: {e}')
197
+ ```
198
+
199
+ ## Core API Surfaces
200
+
201
+ ### Projects
202
+
203
+ #### Get Project
204
+
205
+ ```python
206
+ # Minimal example
207
+ project = jira.project('PROJ')
208
+
209
+ print(project.key)
210
+ print(project.name)
211
+ print(project.lead.displayName)
212
+ ```
213
+
214
+ #### Advanced: Get Project with All Details
215
+
216
+ ```python
217
+ # Get project with all properties
218
+ project = jira.project('PROJ')
219
+
220
+ print('Project Key:', project.key)
221
+ print('Project Name:', project.name)
222
+ print('Project Lead:', project.lead.displayName)
223
+ print('Description:', project.raw.get('description', 'No description'))
224
+ print('Project Type:', project.projectTypeKey)
225
+ print('URL:', project.raw.get('url'))
226
+ ```
227
+
228
+ #### Get All Projects
229
+
230
+ ```python
231
+ # Get all projects
232
+ projects = jira.projects()
233
+
234
+ for project in projects:
235
+ print(f'{project.key}: {project.name}')
236
+ ```
237
+
238
+ #### Get Project Components
239
+
240
+ ```python
241
+ # Get components for a project
242
+ components = jira.project_components('PROJ')
243
+
244
+ for component in components:
245
+ print(f'{component.name}: {component.description}')
246
+ if component.lead:
247
+ print(f' Lead: {component.lead.displayName}')
248
+ ```
249
+
250
+ #### Get Project Versions
251
+
252
+ ```python
253
+ # Get versions/releases for a project
254
+ versions = jira.project_versions('PROJ')
255
+
256
+ for version in versions:
257
+ print(f'{version.name} - Released: {version.released}')
258
+ if hasattr(version, 'releaseDate'):
259
+ print(f' Release Date: {version.releaseDate}')
260
+ ```
261
+
262
+ ### Issues
263
+
264
+ #### Get Issue
265
+
266
+ ```python
267
+ # Minimal example
268
+ issue = jira.issue('PROJ-123')
269
+
270
+ print(issue.key)
271
+ print(issue.fields.summary)
272
+ print(issue.fields.status.name)
273
+ ```
274
+
275
+ #### Advanced: Get Issue with Specific Fields
276
+
277
+ ```python
278
+ # Get issue with expanded fields
279
+ issue = jira.issue(
280
+ 'PROJ-123',
281
+ fields='summary,status,assignee,reporter,created,updated,priority,labels',
282
+ expand='changelog,renderedFields'
283
+ )
284
+
285
+ print('Key:', issue.key)
286
+ print('Summary:', issue.fields.summary)
287
+ print('Status:', issue.fields.status.name)
288
+ print('Priority:', issue.fields.priority.name)
289
+ print('Assignee:', issue.fields.assignee.displayName if issue.fields.assignee else 'Unassigned')
290
+ print('Reporter:', issue.fields.reporter.displayName)
291
+ print('Created:', issue.fields.created)
292
+ print('Updated:', issue.fields.updated)
293
+ print('Labels:', issue.fields.labels)
294
+
295
+ # Access changelog
296
+ if hasattr(issue, 'changelog'):
297
+ print('\nChange History:')
298
+ for history in issue.changelog.histories:
299
+ print(f' {history.created} by {history.author.displayName}')
300
+ ```
301
+
302
+ #### Create Issue
303
+
304
+ ```python
305
+ # Minimal example
306
+ new_issue = jira.create_issue(
307
+ project='PROJ',
308
+ summary='New issue from Python',
309
+ description='This is a test issue created via the API',
310
+ issuetype={'name': 'Task'}
311
+ )
312
+
313
+ print(f'Created issue: {new_issue.key}')
314
+ ```
315
+
316
+ #### Advanced: Create Issue with All Fields
317
+
318
+ ```python
319
+ # Advanced example with multiple fields
320
+ issue_dict = {
321
+ 'project': {'key': 'PROJ'},
322
+ 'summary': 'Implement user authentication',
323
+ 'description': 'Need to implement OAuth 2.0 authentication for the API.\n\nAcceptance Criteria:\n- Support OAuth 2.0\n- Token refresh\n- Secure storage',
324
+ 'issuetype': {'name': 'Story'},
325
+ 'priority': {'name': 'High'},
326
+ 'labels': ['security', 'authentication', 'api'],
327
+ 'components': [{'name': 'Backend'}],
328
+ 'assignee': {'accountId': '5b10a2844c20165700ede21g'},
329
+ 'duedate': '2025-12-31',
330
+ 'customfield_10001': 'Custom field value',
331
+ }
332
+
333
+ new_issue = jira.create_issue(fields=issue_dict)
334
+
335
+ print(f'Created issue: {new_issue.key} - {new_issue.fields.summary}')
336
+ ```
337
+
338
+ #### Bulk Create Issues
339
+
340
+ ```python
341
+ # Create multiple issues at once
342
+ issue_list = [
343
+ {
344
+ 'project': {'key': 'PROJ'},
345
+ 'summary': 'First task',
346
+ 'description': 'Description for first task',
347
+ 'issuetype': {'name': 'Task'},
348
+ },
349
+ {
350
+ 'project': {'key': 'PROJ'},
351
+ 'summary': 'Second task',
352
+ 'description': 'Description for second task',
353
+ 'issuetype': {'name': 'Task'},
354
+ 'priority': {'name': 'High'},
355
+ },
356
+ ]
357
+
358
+ issues = jira.create_issues(field_list=issue_list)
359
+
360
+ for issue in issues:
361
+ print(f'Created: {issue["issue"].key}')
362
+ ```
363
+
364
+ #### Update Issue
365
+
366
+ ```python
367
+ # Update issue fields
368
+ issue = jira.issue('PROJ-123')
369
+
370
+ # Method 1: Using update method
371
+ issue.update(
372
+ summary='Updated summary',
373
+ description='Updated description',
374
+ priority={'name': 'Critical'}
375
+ )
376
+
377
+ # Method 2: Using fields parameter
378
+ issue.update(fields={
379
+ 'labels': ['updated', 'priority'],
380
+ 'customfield_10001': 'New value'
381
+ })
382
+
383
+ # Method 3: Silent update (no notifications)
384
+ issue.update(
385
+ notify=False,
386
+ description='Silent update - no emails sent'
387
+ )
388
+
389
+ print('Issue updated successfully')
390
+ ```
391
+
392
+ #### Assign Issue
393
+
394
+ ```python
395
+ # Assign issue to a user
396
+ jira.assign_issue('PROJ-123', 'username')
397
+
398
+ # Or use accountId
399
+ jira.assign_issue('PROJ-123', '5b10a2844c20165700ede21g')
400
+
401
+ # Unassign issue
402
+ jira.assign_issue('PROJ-123', None)
403
+
404
+ print('Issue assigned successfully')
405
+ ```
406
+
407
+ #### Delete Issue
408
+
409
+ ```python
410
+ # Delete an issue
411
+ issue = jira.issue('PROJ-123')
412
+ issue.delete()
413
+
414
+ # Delete with subtasks
415
+ issue.delete(deleteSubtasks=True)
416
+
417
+ print('Issue deleted successfully')
418
+ ```
419
+
420
+ ### Issue Search
421
+
422
+ #### Basic JQL Search
423
+
424
+ ```python
425
+ # Search for issues in a project
426
+ issues = jira.search_issues('project = PROJ')
427
+
428
+ for issue in issues:
429
+ print(f'{issue.key}: {issue.fields.summary}')
430
+ ```
431
+
432
+ #### Advanced: Search with Pagination and Options
433
+
434
+ ```python
435
+ # Advanced search with pagination and field selection
436
+ issues = jira.search_issues(
437
+ jql_str='project = PROJ AND status = "In Progress" ORDER BY created DESC',
438
+ startAt=0,
439
+ maxResults=50,
440
+ fields='summary,status,assignee,priority,created',
441
+ expand='changelog'
442
+ )
443
+
444
+ print(f'Total issues: {issues.total}')
445
+ print(f'Returned: {len(issues)}')
446
+
447
+ for issue in issues:
448
+ print(f'{issue.key}: {issue.fields.summary}')
449
+ print(f' Status: {issue.fields.status.name}')
450
+ print(f' Priority: {issue.fields.priority.name}')
451
+ print(f' Created: {issue.fields.created}')
452
+ ```
453
+
454
+ #### Pagination Example
455
+
456
+ ```python
457
+ # Fetch all issues with pagination
458
+ def get_all_issues(jira, jql):
459
+ """Fetch all issues matching JQL query with pagination"""
460
+ all_issues = []
461
+ start_at = 0
462
+ max_results = 100
463
+
464
+ while True:
465
+ issues = jira.search_issues(
466
+ jql_str=jql,
467
+ startAt=start_at,
468
+ maxResults=max_results
469
+ )
470
+
471
+ all_issues.extend(issues)
472
+
473
+ print(f'Fetched {len(all_issues)} of {issues.total} issues')
474
+
475
+ if len(issues) < max_results:
476
+ break
477
+
478
+ start_at += max_results
479
+
480
+ return all_issues
481
+
482
+ # Usage
483
+ all_project_issues = get_all_issues(jira, 'project = PROJ')
484
+ print(f'Total issues fetched: {len(all_project_issues)}')
485
+ ```
486
+
487
+ #### Complex JQL Queries
488
+
489
+ ```python
490
+ # Complex JQL with multiple conditions
491
+ jql = """
492
+ project = PROJ AND
493
+ status IN ("To Do", "In Progress") AND
494
+ priority IN (High, Critical) AND
495
+ assignee = currentUser() AND
496
+ created >= -30d
497
+ ORDER BY priority DESC, created ASC
498
+ """
499
+
500
+ issues = jira.search_issues(jql.strip(), maxResults=100)
501
+
502
+ print(f'High priority issues: {len(issues)}')
503
+ for issue in issues:
504
+ print(f'{issue.key}: {issue.fields.summary} ({issue.fields.priority.name})')
505
+ ```
506
+
507
+ #### Common JQL Patterns
508
+
509
+ ```python
510
+ # Issues assigned to current user
511
+ issues = jira.search_issues('assignee = currentUser()')
512
+
513
+ # Issues created in last 7 days
514
+ issues = jira.search_issues('created >= -7d')
515
+
516
+ # High priority unresolved bugs
517
+ issues = jira.search_issues('type = Bug AND priority = High AND status != Resolved')
518
+
519
+ # Issues in specific sprint
520
+ issues = jira.search_issues('sprint = 123')
521
+
522
+ # Issues with specific labels
523
+ issues = jira.search_issues('labels IN (urgent, critical)')
524
+
525
+ # Issues due this week
526
+ issues = jira.search_issues('duedate >= startOfWeek() AND duedate <= endOfWeek()')
527
+
528
+ # Combining multiple conditions
529
+ issues = jira.search_issues(
530
+ 'project = PROJ AND issuetype in (Story, Task) AND status = Open',
531
+ maxResults=50
532
+ )
533
+ ```
534
+
535
+ ### Comments
536
+
537
+ #### Get Comments
538
+
539
+ ```python
540
+ # Get all comments for an issue
541
+ issue = jira.issue('PROJ-123')
542
+ comments = issue.fields.comment.comments
543
+
544
+ for comment in comments:
545
+ print(f'{comment.author.displayName} at {comment.created}:')
546
+ print(f' {comment.body}')
547
+ ```
548
+
549
+ #### Get Specific Comment
550
+
551
+ ```python
552
+ # Get a specific comment by ID
553
+ comment = jira.comment('PROJ-123', '10234')
554
+
555
+ print(f'Author: {comment.author.displayName}')
556
+ print(f'Created: {comment.created}')
557
+ print(f'Body: {comment.body}')
558
+ ```
559
+
560
+ #### Add Comment
561
+
562
+ ```python
563
+ # Add a simple comment
564
+ comment = jira.add_comment('PROJ-123', 'This is a new comment from the API')
565
+
566
+ print(f'Comment added: {comment.id}')
567
+ ```
568
+
569
+ #### Advanced: Add Comment with Visibility
570
+
571
+ ```python
572
+ # Add comment visible only to specific role
573
+ comment = jira.add_comment(
574
+ 'PROJ-123',
575
+ 'This is visible only to administrators',
576
+ visibility={'type': 'role', 'value': 'Administrators'}
577
+ )
578
+
579
+ # Add comment visible to specific group
580
+ comment = jira.add_comment(
581
+ 'PROJ-123',
582
+ 'This is visible only to developers',
583
+ visibility={'type': 'group', 'value': 'jira-developers'}
584
+ )
585
+
586
+ print(f'Restricted comment added: {comment.id}')
587
+ ```
588
+
589
+ #### Add Comment with Rich Text (ADF Format)
590
+
591
+ ```python
592
+ # Add comment using Atlassian Document Format (ADF)
593
+ comment_adf = {
594
+ "type": "doc",
595
+ "version": 1,
596
+ "content": [
597
+ {
598
+ "type": "paragraph",
599
+ "content": [
600
+ {
601
+ "type": "text",
602
+ "text": "This is a comment with "
603
+ },
604
+ {
605
+ "type": "text",
606
+ "text": "bold text",
607
+ "marks": [{"type": "strong"}]
608
+ }
609
+ ]
610
+ },
611
+ {
612
+ "type": "codeBlock",
613
+ "content": [
614
+ {
615
+ "type": "text",
616
+ "text": "print('Hello, World!')"
617
+ }
618
+ ]
619
+ }
620
+ ]
621
+ }
622
+
623
+ comment = jira.add_comment('PROJ-123', comment_adf)
624
+ print(f'Rich text comment added: {comment.id}')
625
+ ```
626
+
627
+ #### Update Comment
628
+
629
+ ```python
630
+ # Update an existing comment
631
+ comment = jira.comment('PROJ-123', '10234')
632
+
633
+ comment.update(body='Updated comment text')
634
+
635
+ # Silent update (no notifications)
636
+ comment.update(body='Quiet update', notify=False)
637
+
638
+ print('Comment updated successfully')
639
+ ```
640
+
641
+ #### Delete Comment
642
+
643
+ ```python
644
+ # Delete a comment
645
+ comment = jira.comment('PROJ-123', '10234')
646
+ comment.delete()
647
+
648
+ print('Comment deleted successfully')
649
+ ```
650
+
651
+ ### Transitions
652
+
653
+ #### Get Available Transitions
654
+
655
+ ```python
656
+ # Get all available transitions for an issue
657
+ transitions = jira.transitions('PROJ-123')
658
+
659
+ print('Available transitions:')
660
+ for transition in transitions:
661
+ print(f" {transition['id']}: {transition['name']} -> {transition['to']['name']}")
662
+ ```
663
+
664
+ #### Transition Issue
665
+
666
+ ```python
667
+ # Transition issue to a new status
668
+ jira.transition_issue('PROJ-123', '21') # Use transition ID
669
+
670
+ print('Issue transitioned successfully')
671
+ ```
672
+
673
+ #### Advanced: Transition with Fields
674
+
675
+ ```python
676
+ # Transition issue and set fields
677
+ jira.transition_issue(
678
+ 'PROJ-123',
679
+ '5', # Transition ID for "Resolve Issue"
680
+ assignee={'name': 'pm_user'},
681
+ resolution={'id': '3'}, # Fixed
682
+ comment='Issue resolved and tested'
683
+ )
684
+
685
+ # Alternative using fields parameter
686
+ jira.transition_issue(
687
+ 'PROJ-123',
688
+ '5',
689
+ fields={
690
+ 'assignee': {'name': 'qa_user'},
691
+ 'resolution': {'name': 'Done'},
692
+ 'customfield_10001': 'custom value'
693
+ }
694
+ )
695
+
696
+ print('Issue transitioned with fields updated')
697
+ ```
698
+
699
+ ### Attachments
700
+
701
+ #### Add Attachment
702
+
703
+ ```python
704
+ # Add attachment from file path
705
+ jira.add_attachment(
706
+ issue='PROJ-123',
707
+ attachment='/path/to/file.pdf'
708
+ )
709
+
710
+ print('Attachment added successfully')
711
+ ```
712
+
713
+ #### Advanced: Add Multiple Attachments
714
+
715
+ ```python
716
+ # Add multiple attachments
717
+ jira.add_attachment(
718
+ issue='PROJ-123',
719
+ attachment='/path/to/document.pdf',
720
+ filename='report.pdf'
721
+ )
722
+
723
+ # Add attachment from file object
724
+ with open('/path/to/file.txt', 'rb') as f:
725
+ jira.add_attachment(issue='PROJ-123', attachment=f, filename='data.txt')
726
+
727
+ # Add multiple files
728
+ attachments = ['/path/to/file1.txt', '/path/to/file2.pdf']
729
+ for attachment_path in attachments:
730
+ jira.add_attachment(issue='PROJ-123', attachment=attachment_path)
731
+
732
+ print('Multiple attachments added successfully')
733
+ ```
734
+
735
+ #### Get Attachments
736
+
737
+ ```python
738
+ # Get all attachments for an issue
739
+ issue = jira.issue('PROJ-123')
740
+
741
+ for attachment in issue.fields.attachment:
742
+ print(f'Filename: {attachment.filename}')
743
+ print(f'Size: {attachment.size} bytes')
744
+ print(f'MIME Type: {attachment.mimeType}')
745
+ print(f'Created: {attachment.created}')
746
+ print(f'Download URL: {attachment.content}')
747
+ print()
748
+ ```
749
+
750
+ #### Download Attachment
751
+
752
+ ```python
753
+ # Download attachment content
754
+ issue = jira.issue('PROJ-123')
755
+
756
+ for attachment in issue.fields.attachment:
757
+ # Get attachment content
758
+ content = attachment.get()
759
+
760
+ # Save to file
761
+ with open(attachment.filename, 'wb') as f:
762
+ f.write(content)
763
+
764
+ print(f'Downloaded: {attachment.filename}')
765
+ ```
766
+
767
+ #### Delete Attachment
768
+
769
+ ```python
770
+ # Delete an attachment by ID
771
+ jira.delete_attachment('10001')
772
+
773
+ print('Attachment deleted successfully')
774
+ ```
775
+
776
+ ### Agile - Boards
777
+
778
+ #### Get All Boards
779
+
780
+ ```python
781
+ # Get all boards
782
+ boards = jira.boards()
783
+
784
+ for board in boards:
785
+ print(f'{board.id}: {board.name} ({board.type})')
786
+ ```
787
+
788
+ #### Advanced: Get Boards with Filters
789
+
790
+ ```python
791
+ # Get boards with specific parameters
792
+ boards = jira.boards(
793
+ startAt=0,
794
+ maxResults=50,
795
+ type='scrum', # or 'kanban'
796
+ name='Development',
797
+ projectKeyOrID='PROJ'
798
+ )
799
+
800
+ for board in boards:
801
+ print(f'{board.id}: {board.name}')
802
+ print(f' Type: {board.type}')
803
+ if hasattr(board, 'location'):
804
+ print(f' Location: {board.location.name}')
805
+ ```
806
+
807
+ #### Get Specific Board
808
+
809
+ ```python
810
+ # Get a specific board by ID
811
+ board = jira.board(1)
812
+
813
+ print(f'Board: {board.name}')
814
+ print(f'Type: {board.type}')
815
+ print(f'Location: {board.location.name}')
816
+ ```
817
+
818
+ #### Get Issues on Board
819
+
820
+ ```python
821
+ # Get all issues on a board
822
+ board_id = 1
823
+ issues = jira.search_issues(f'board = {board_id}')
824
+
825
+ for issue in issues:
826
+ print(f'{issue.key}: {issue.fields.summary}')
827
+ ```
828
+
829
+ ### Agile - Sprints
830
+
831
+ #### Get Sprints for Board
832
+
833
+ ```python
834
+ # Get all sprints for a board
835
+ board_id = 1
836
+ sprints = jira.sprints(board_id)
837
+
838
+ for sprint in sprints:
839
+ print(f'{sprint.id}: {sprint.name}')
840
+ print(f' State: {sprint.state}')
841
+ if hasattr(sprint, 'startDate'):
842
+ print(f' Start: {sprint.startDate}')
843
+ if hasattr(sprint, 'endDate'):
844
+ print(f' End: {sprint.endDate}')
845
+ ```
846
+
847
+ #### Advanced: Get Sprints with Filters
848
+
849
+ ```python
850
+ # Get sprints with specific state
851
+ board_id = 1
852
+ sprints = jira.sprints(
853
+ board_id,
854
+ startAt=0,
855
+ maxResults=50,
856
+ state='active' # 'active', 'closed', 'future'
857
+ )
858
+
859
+ for sprint in sprints:
860
+ print(f'{sprint.name} - {sprint.state}')
861
+ ```
862
+
863
+ #### Get Sprint
864
+
865
+ ```python
866
+ # Get a specific sprint by ID
867
+ sprint = jira.sprint(123)
868
+
869
+ print(f'Sprint: {sprint.name}')
870
+ print(f'State: {sprint.state}')
871
+ print(f'Start Date: {sprint.startDate}')
872
+ print(f'End Date: {sprint.endDate}')
873
+ print(f'Goal: {sprint.goal}')
874
+ ```
875
+
876
+ #### Get Issues in Sprint
877
+
878
+ ```python
879
+ # Get all issues in a sprint
880
+ sprint_id = 123
881
+ issues = jira.search_issues(f'sprint = {sprint_id}')
882
+
883
+ print(f'Issues in sprint: {len(issues)}')
884
+ for issue in issues:
885
+ print(f'{issue.key}: {issue.fields.summary}')
886
+ print(f' Status: {issue.fields.status.name}')
887
+ ```
888
+
889
+ #### Advanced: Get Sprint Issues with JQL
890
+
891
+ ```python
892
+ # Get incomplete issues in active sprint
893
+ sprint_id = 123
894
+ jql = f'sprint = {sprint_id} AND status != Done'
895
+
896
+ issues = jira.search_issues(jql, maxResults=100)
897
+
898
+ print(f'Incomplete issues: {len(issues)}')
899
+ for issue in issues:
900
+ print(f'{issue.key}: {issue.fields.summary} ({issue.fields.status.name})')
901
+ ```
902
+
903
+ ### Users
904
+
905
+ #### Get Current User
906
+
907
+ ```python
908
+ # Get currently authenticated user
909
+ current_user = jira.current_user()
910
+
911
+ print(f'Username: {current_user}')
912
+ ```
913
+
914
+ #### Search for Users
915
+
916
+ ```python
917
+ # Search for users by username or email
918
+ users = jira.search_users(user='john')
919
+
920
+ for user in users:
921
+ print(f'{user.displayName} ({user.emailAddress})')
922
+ print(f' Account ID: {user.accountId}')
923
+ print(f' Active: {user.active}')
924
+ ```
925
+
926
+ #### Advanced: Search Users with Filters
927
+
928
+ ```python
929
+ # Search for users in a project
930
+ users = jira.search_users(
931
+ user='john',
932
+ startAt=0,
933
+ maxResults=50,
934
+ includeActive=True,
935
+ includeInactive=False
936
+ )
937
+
938
+ for user in users:
939
+ print(f'{user.displayName} - {user.emailAddress}')
940
+ ```
941
+
942
+ #### Search Assignable Users
943
+
944
+ ```python
945
+ # Search for users who can be assigned to issues in a project
946
+ users = jira.search_assignable_users_for_projects(
947
+ username='', # Empty string to get all
948
+ projectKeys='PROJ',
949
+ startAt=0,
950
+ maxResults=50
951
+ )
952
+
953
+ for user in users:
954
+ print(f'{user.displayName} can be assigned to PROJ')
955
+ ```
956
+
957
+ ### Watchers
958
+
959
+ #### Get Watchers
960
+
961
+ ```python
962
+ # Get watchers for an issue
963
+ issue = jira.issue('PROJ-123')
964
+ watchers = jira.watchers(issue)
965
+
966
+ print(f'Watch Count: {watchers.watchCount}')
967
+ print('Watchers:')
968
+ for watcher in watchers.watchers:
969
+ print(f' {watcher.displayName}')
970
+ ```
971
+
972
+ #### Add Watcher
973
+
974
+ ```python
975
+ # Add a watcher to an issue
976
+ jira.add_watcher('PROJ-123', 'username')
977
+
978
+ # Or use accountId
979
+ jira.add_watcher('PROJ-123', '5b10a2844c20165700ede21g')
980
+
981
+ print('Watcher added successfully')
982
+ ```
983
+
984
+ #### Remove Watcher
985
+
986
+ ```python
987
+ # Remove a watcher from an issue
988
+ jira.remove_watcher('PROJ-123', 'username')
989
+
990
+ print('Watcher removed successfully')
991
+ ```
992
+
993
+ ### Labels
994
+
995
+ #### Get Issue Labels
996
+
997
+ ```python
998
+ # Get labels for an issue
999
+ issue = jira.issue('PROJ-123')
1000
+ labels = issue.fields.labels
1001
+
1002
+ print('Labels:', labels)
1003
+ ```
1004
+
1005
+ #### Update Labels
1006
+
1007
+ ```python
1008
+ # Add labels to an issue
1009
+ issue = jira.issue('PROJ-123')
1010
+ issue.update(fields={'labels': ['backend', 'api', 'urgent']})
1011
+
1012
+ # Append labels (preserve existing)
1013
+ existing_labels = issue.fields.labels
1014
+ new_labels = existing_labels + ['new-label']
1015
+ issue.update(fields={'labels': new_labels})
1016
+
1017
+ print('Labels updated successfully')
1018
+ ```
1019
+
1020
+ ### Filters
1021
+
1022
+ #### Get Favorite Filters
1023
+
1024
+ ```python
1025
+ # Get all favorite filters for current user
1026
+ filters = jira.favourite_filters()
1027
+
1028
+ for f in filters:
1029
+ print(f'{f.id}: {f.name}')
1030
+ print(f' JQL: {f.jql}')
1031
+ print(f' Owner: {f.owner.displayName}')
1032
+ ```
1033
+
1034
+ #### Get Filter
1035
+
1036
+ ```python
1037
+ # Get a specific filter by ID
1038
+ filter_obj = jira.filter('10000')
1039
+
1040
+ print(f'Filter: {filter_obj.name}')
1041
+ print(f'JQL: {filter_obj.jql}')
1042
+ print(f'Owner: {filter_obj.owner.displayName}')
1043
+ ```
1044
+
1045
+ #### Search Using Filter
1046
+
1047
+ ```python
1048
+ # Execute a saved filter
1049
+ filter_id = 10000
1050
+ jql = f'filter = {filter_id}'
1051
+ issues = jira.search_issues(jql, maxResults=100)
1052
+
1053
+ print(f'Filter results: {len(issues)} issues')
1054
+ ```
1055
+
1056
+ ### Versions (Releases)
1057
+
1058
+ #### Create Version
1059
+
1060
+ ```python
1061
+ # Create a new version/release
1062
+ version = jira.create_version(
1063
+ name='v1.2.0',
1064
+ project='PROJ',
1065
+ description='Q4 2025 Release',
1066
+ releaseDate='2025-12-15',
1067
+ archived=False,
1068
+ released=False
1069
+ )
1070
+
1071
+ print(f'Created version: {version.name} (ID: {version.id})')
1072
+ ```
1073
+
1074
+ #### Get Version
1075
+
1076
+ ```python
1077
+ # Get a specific version
1078
+ version = jira.version('10001')
1079
+
1080
+ print(f'Version: {version.name}')
1081
+ print(f'Released: {version.released}')
1082
+ if hasattr(version, 'releaseDate'):
1083
+ print(f'Release Date: {version.releaseDate}')
1084
+ ```
1085
+
1086
+ #### Update Version
1087
+
1088
+ ```python
1089
+ # Update a version
1090
+ version = jira.version('10001')
1091
+ version.update(
1092
+ name='v1.2.1',
1093
+ description='Updated release',
1094
+ released=True,
1095
+ releaseDate='2025-12-20'
1096
+ )
1097
+
1098
+ print('Version updated successfully')
1099
+ ```
1100
+
1101
+ #### Delete Version
1102
+
1103
+ ```python
1104
+ # Delete a version
1105
+ version = jira.version('10001')
1106
+ version.delete()
1107
+
1108
+ print('Version deleted successfully')
1109
+ ```
1110
+
1111
+ ### Components
1112
+
1113
+ #### Create Component
1114
+
1115
+ ```python
1116
+ # Create a new component
1117
+ component = jira.create_component(
1118
+ name='Frontend',
1119
+ project='PROJ',
1120
+ description='Frontend components and pages',
1121
+ leadAccountId='5b10a2844c20165700ede21g'
1122
+ )
1123
+
1124
+ print(f'Created component: {component.name} (ID: {component.id})')
1125
+ ```
1126
+
1127
+ #### Get Component
1128
+
1129
+ ```python
1130
+ # Get a specific component
1131
+ component = jira.component('10000')
1132
+
1133
+ print(f'Component: {component.name}')
1134
+ print(f'Description: {component.description}')
1135
+ if component.lead:
1136
+ print(f'Lead: {component.lead.displayName}')
1137
+ ```
1138
+
1139
+ #### Update Component
1140
+
1141
+ ```python
1142
+ # Update a component
1143
+ component = jira.component('10000')
1144
+ component.update(
1145
+ name='Updated Frontend',
1146
+ description='Updated description'
1147
+ )
1148
+
1149
+ print('Component updated successfully')
1150
+ ```
1151
+
1152
+ ### Workflows
1153
+
1154
+ #### Get Workflows
1155
+
1156
+ ```python
1157
+ # Get all workflows (requires project admin permissions)
1158
+ workflows = jira.workflows()
1159
+
1160
+ for workflow in workflows:
1161
+ print(f'{workflow.name}')
1162
+ ```
1163
+
1164
+ ### Custom Fields
1165
+
1166
+ #### Get All Fields
1167
+
1168
+ ```python
1169
+ # Get all fields in Jira
1170
+ fields = jira.fields()
1171
+
1172
+ print('Custom Fields:')
1173
+ for field in fields:
1174
+ if field['custom']:
1175
+ print(f"{field['id']}: {field['name']}")
1176
+ ```
1177
+
1178
+ #### Get Custom Field Value
1179
+
1180
+ ```python
1181
+ # Get custom field value from an issue
1182
+ issue = jira.issue('PROJ-123')
1183
+ custom_field_id = 'customfield_10001'
1184
+
1185
+ if hasattr(issue.fields, custom_field_id):
1186
+ value = getattr(issue.fields, custom_field_id)
1187
+ print(f'Custom field value: {value}')
1188
+ ```
1189
+
1190
+ #### Set Custom Field Value
1191
+
1192
+ ```python
1193
+ # Set custom field value
1194
+ issue = jira.issue('PROJ-123')
1195
+ issue.update(fields={'customfield_10001': 'New custom value'})
1196
+
1197
+ print('Custom field updated successfully')
1198
+ ```
1199
+
1200
+ ### Permissions
1201
+
1202
+ #### Get My Permissions
1203
+
1204
+ ```python
1205
+ # Get permissions for current user
1206
+ permissions = jira.my_permissions(projectKey='PROJ')
1207
+
1208
+ print('Permissions for project PROJ:')
1209
+ for key, permission in permissions['permissions'].items():
1210
+ if permission['havePermission']:
1211
+ print(f' {key}: Granted')
1212
+ ```
1213
+
1214
+ #### Check Specific Permission
1215
+
1216
+ ```python
1217
+ # Check if user has specific permission
1218
+ permissions = jira.my_permissions(
1219
+ projectKey='PROJ',
1220
+ permissions='CREATE_ISSUES,EDIT_ISSUES'
1221
+ )
1222
+
1223
+ can_create = permissions['permissions']['CREATE_ISSUES']['havePermission']
1224
+ can_edit = permissions['permissions']['EDIT_ISSUES']['havePermission']
1225
+
1226
+ print(f'Can create issues: {can_create}')
1227
+ print(f'Can edit issues: {can_edit}')
1228
+ ```
1229
+
1230
+ ## Common Patterns
1231
+
1232
+ ### Batch Operations
1233
+
1234
+ ```python
1235
+ # Create multiple issues efficiently
1236
+ def create_multiple_issues(jira, issues_data):
1237
+ """Create multiple issues using bulk operation"""
1238
+ return jira.create_issues(field_list=issues_data)
1239
+
1240
+ # Usage
1241
+ issues_data = [
1242
+ {
1243
+ 'project': {'key': 'PROJ'},
1244
+ 'summary': f'Task {i}',
1245
+ 'issuetype': {'name': 'Task'},
1246
+ }
1247
+ for i in range(1, 11)
1248
+ ]
1249
+
1250
+ created_issues = create_multiple_issues(jira, issues_data)
1251
+
1252
+ for result in created_issues:
1253
+ if 'issue' in result:
1254
+ print(f"Created: {result['issue'].key}")
1255
+ else:
1256
+ print(f"Error: {result['error']}")
1257
+ ```
1258
+
1259
+ ### Rate Limiting
1260
+
1261
+ ```python
1262
+ import time
1263
+
1264
+ def rate_limited_operation(items, operation, rate_limit=10, delay=1):
1265
+ """Execute operations with rate limiting"""
1266
+ results = []
1267
+
1268
+ for i, item in enumerate(items):
1269
+ result = operation(item)
1270
+ results.append(result)
1271
+
1272
+ # Add delay every rate_limit operations
1273
+ if (i + 1) % rate_limit == 0 and i < len(items) - 1:
1274
+ print(f'Processed {i + 1} items, waiting {delay}s...')
1275
+ time.sleep(delay)
1276
+
1277
+ return results
1278
+
1279
+ # Usage
1280
+ def update_issue(issue_key):
1281
+ issue = jira.issue(issue_key)
1282
+ issue.update(fields={'labels': ['batch-updated']})
1283
+ return issue_key
1284
+
1285
+ issue_keys = ['PROJ-1', 'PROJ-2', 'PROJ-3', 'PROJ-4', 'PROJ-5']
1286
+ results = rate_limited_operation(issue_keys, update_issue, rate_limit=2, delay=1)
1287
+ ```
1288
+
1289
+ ### Retry Logic
1290
+
1291
+ ```python
1292
+ import time
1293
+ from jira import JIRAError
1294
+
1295
+ def with_retry(operation, max_retries=3, delay=1):
1296
+ """Execute operation with retry logic"""
1297
+ for attempt in range(1, max_retries + 1):
1298
+ try:
1299
+ return operation()
1300
+ except JIRAError as e:
1301
+ if attempt == max_retries:
1302
+ raise
1303
+
1304
+ print(f'Attempt {attempt} failed: {e.text}')
1305
+ print(f'Retrying in {delay}s...')
1306
+ time.sleep(delay)
1307
+ delay *= 2 # Exponential backoff
1308
+
1309
+ # Usage
1310
+ issue = with_retry(lambda: jira.issue('PROJ-123'))
1311
+ print(f'Retrieved: {issue.key}')
1312
+ ```
1313
+
1314
+ ### Context Manager for Connection
1315
+
1316
+ ```python
1317
+ from contextlib import contextmanager
1318
+
1319
+ @contextmanager
1320
+ def jira_connection(server, email, api_token):
1321
+ """Context manager for Jira connection"""
1322
+ jira = JIRA(
1323
+ server=server,
1324
+ basic_auth=(email, api_token)
1325
+ )
1326
+ try:
1327
+ yield jira
1328
+ finally:
1329
+ jira.close()
1330
+
1331
+ # Usage
1332
+ with jira_connection(
1333
+ 'https://your-domain.atlassian.net',
1334
+ 'your@email.com',
1335
+ 'your_token'
1336
+ ) as jira:
1337
+ issues = jira.search_issues('project = PROJ', maxResults=10)
1338
+ for issue in issues:
1339
+ print(f'{issue.key}: {issue.fields.summary}')
1340
+ ```
1341
+
1342
+ ## Complete Application Example
1343
+
1344
+ ```python
1345
+ import os
1346
+ from jira import JIRA, JIRAError
1347
+
1348
+ def main():
1349
+ # Initialize Jira client
1350
+ jira = JIRA(
1351
+ server=os.environ.get('JIRA_SERVER', 'https://your-domain.atlassian.net'),
1352
+ basic_auth=(
1353
+ os.environ.get('JIRA_EMAIL'),
1354
+ os.environ.get('JIRA_API_TOKEN')
1355
+ )
1356
+ )
1357
+
1358
+ try:
1359
+ # Get current user
1360
+ current_user = jira.current_user()
1361
+ print(f'Logged in as: {current_user}')
1362
+
1363
+ # Create a new issue
1364
+ new_issue = jira.create_issue(
1365
+ project='PROJ',
1366
+ summary='Implement new feature',
1367
+ description='This is a new feature request from the API',
1368
+ issuetype={'name': 'Story'},
1369
+ priority={'name': 'High'}
1370
+ )
1371
+
1372
+ print(f'Created issue: {new_issue.key}')
1373
+
1374
+ # Add a comment
1375
+ comment = jira.add_comment(
1376
+ new_issue.key,
1377
+ 'Starting work on this issue'
1378
+ )
1379
+
1380
+ print(f'Comment added: {comment.id}')
1381
+
1382
+ # Search for issues
1383
+ jql = 'project = PROJ AND status = "To Do" ORDER BY created DESC'
1384
+ issues = jira.search_issues(jql, maxResults=10)
1385
+
1386
+ print(f'\nFound {issues.total} issues:')
1387
+ for issue in issues:
1388
+ print(f' {issue.key}: {issue.fields.summary}')
1389
+ print(f' Status: {issue.fields.status.name}')
1390
+ print(f' Priority: {issue.fields.priority.name}')
1391
+
1392
+ # Get available transitions
1393
+ transitions = jira.transitions(new_issue.key)
1394
+ print(f'\nAvailable transitions for {new_issue.key}:')
1395
+ for t in transitions:
1396
+ print(f" {t['id']}: {t['name']}")
1397
+
1398
+ except JIRAError as e:
1399
+ print(f'JIRA Error: {e.status_code} - {e.text}')
1400
+ except Exception as e:
1401
+ print(f'Error: {e}')
1402
+ finally:
1403
+ jira.close()
1404
+
1405
+ if __name__ == '__main__':
1406
+ main()
1407
+ ```
1408
+
1409
+ ## Advanced Features
1410
+
1411
+ ### Custom JQL Functions
1412
+
1413
+ ```python
1414
+ # Use JQL functions for dynamic queries
1415
+ recent_issues = jira.search_issues('created >= startOfWeek()')
1416
+ my_overdue = jira.search_issues('assignee = currentUser() AND duedate < now()')
1417
+ unresolved_bugs = jira.search_issues('resolution = Unresolved AND type = Bug')
1418
+ ```
1419
+
1420
+ ### Issue Links
1421
+
1422
+ ```python
1423
+ # Create issue link
1424
+ jira.create_issue_link(
1425
+ type='Blocks',
1426
+ inwardIssue='PROJ-123',
1427
+ outwardIssue='PROJ-124'
1428
+ )
1429
+
1430
+ # Get issue links
1431
+ issue = jira.issue('PROJ-123')
1432
+ for link in issue.fields.issuelinks:
1433
+ if hasattr(link, 'outwardIssue'):
1434
+ print(f'Blocks: {link.outwardIssue.key}')
1435
+ if hasattr(link, 'inwardIssue'):
1436
+ print(f'Blocked by: {link.inwardIssue.key}')
1437
+ ```
1438
+
1439
+ ### Work Logs
1440
+
1441
+ ```python
1442
+ # Add work log to issue
1443
+ worklog = jira.add_worklog(
1444
+ issue='PROJ-123',
1445
+ timeSpent='2h 30m',
1446
+ comment='Worked on implementation'
1447
+ )
1448
+
1449
+ # Get work logs
1450
+ issue = jira.issue('PROJ-123')
1451
+ worklogs = jira.worklogs(issue)
1452
+
1453
+ for wl in worklogs:
1454
+ print(f'{wl.author.displayName}: {wl.timeSpent}')
1455
+ ```
1456
+
1457
+ ## Response Format Examples
1458
+
1459
+ ### Issue Object
1460
+
1461
+ ```python
1462
+ {
1463
+ 'key': 'PROJ-123',
1464
+ 'id': '10002',
1465
+ 'fields': {
1466
+ 'summary': 'Issue summary',
1467
+ 'description': 'Issue description',
1468
+ 'status': {'name': 'In Progress'},
1469
+ 'priority': {'name': 'High'},
1470
+ 'assignee': {
1471
+ 'displayName': 'John Doe',
1472
+ 'emailAddress': 'john@example.com',
1473
+ 'accountId': '5b10a2844c20165700ede21g'
1474
+ },
1475
+ 'reporter': {'displayName': 'Jane Smith'},
1476
+ 'created': '2025-11-01T10:00:00.000+0000',
1477
+ 'updated': '2025-11-07T15:30:00.000+0000',
1478
+ 'labels': ['backend', 'api']
1479
+ }
1480
+ }
1481
+ ```
1482
+
1483
+ ### Search Results
1484
+
1485
+ ```python
1486
+ {
1487
+ 'startAt': 0,
1488
+ 'maxResults': 50,
1489
+ 'total': 127,
1490
+ 'issues': [<Issue object>, <Issue object>, ...]
1491
+ }
1492
+ ```