Backport provider package for JIRA integration with Apache Airflow 1.10.x, providing operators, sensors, and hooks for interacting with JIRA systems.
npx @tessl/cli install tessl/pypi-apache-airflow-backport-providers-jira@2020.10.00
# Apache Airflow JIRA Provider
1
2
A backport provider package for Apache Airflow 1.10.x that enables workflow orchestration systems to integrate with JIRA issue tracking and project management. Provides comprehensive operators for creating and updating tickets, sensors for monitoring ticket status and state changes, and hooks for direct API interactions with JIRA instances.
3
4
## Package Information
5
6
- **Package Name**: apache-airflow-backport-providers-jira
7
- **Package Type**: pip
8
- **Language**: Python
9
- **Installation**: `pip install apache-airflow-backport-providers-jira`
10
- **Dependencies**: `apache-airflow~=1.10`, `JIRA>1.0.7`
11
12
## Core Imports
13
14
```python
15
from airflow.providers.jira.hooks.jira import JiraHook
16
from airflow.providers.jira.operators.jira import JiraOperator
17
from airflow.providers.jira.sensors.jira import JiraSensor, JiraTicketSensor
18
```
19
20
## Basic Usage
21
22
```python
23
from airflow import DAG
24
from airflow.providers.jira.hooks.jira import JiraHook
25
from airflow.providers.jira.operators.jira import JiraOperator
26
from airflow.providers.jira.sensors.jira import JiraTicketSensor
27
from datetime import datetime, timedelta
28
29
# Create a DAG
30
dag = DAG(
31
'jira_integration_example',
32
default_args={
33
'owner': 'data_team',
34
'retries': 1,
35
'retry_delay': timedelta(minutes=5),
36
},
37
description='Example JIRA integration workflow',
38
schedule_interval='@daily',
39
start_date=datetime(2023, 1, 1),
40
catchup=False,
41
)
42
43
# Create a JIRA issue using JiraOperator
44
create_ticket = JiraOperator(
45
task_id='create_jira_ticket',
46
jira_conn_id='jira_default',
47
jira_method='create_issue',
48
jira_method_args={
49
'fields': {
50
'project': {'key': 'TEST'},
51
'summary': 'Daily workflow completed',
52
'description': 'Automated ticket from Airflow workflow',
53
'issuetype': {'name': 'Task'},
54
}
55
},
56
dag=dag,
57
)
58
59
# Monitor ticket status using JiraTicketSensor
60
wait_for_approval = JiraTicketSensor(
61
task_id='wait_for_ticket_approval',
62
jira_conn_id='jira_default',
63
ticket_id='TEST-123',
64
field='status',
65
expected_value='In Progress',
66
poke_interval=300, # Check every 5 minutes
67
timeout=3600, # Timeout after 1 hour
68
dag=dag,
69
)
70
71
# Set task dependencies
72
create_ticket >> wait_for_approval
73
```
74
75
## Architecture
76
77
The JIRA provider follows Apache Airflow's standard architecture pattern with three main component types:
78
79
- **Hooks**: Manage connections and low-level API interactions with JIRA
80
- **Operators**: Execute JIRA operations as workflow tasks
81
- **Sensors**: Monitor JIRA state changes and wait for conditions
82
83
All components support Airflow's connection management system for secure credential handling and support templating for dynamic parameter substitution.
84
85
## Capabilities
86
87
### Connection Management
88
89
Establishes and manages connections to JIRA instances with authentication, SSL verification, and proxy support.
90
91
```python { .api }
92
class JiraHook(BaseHook):
93
def __init__(self, jira_conn_id: str = 'jira_default', proxies: Optional[Any] = None) -> None:
94
"""
95
Initialize JIRA hook for API interactions.
96
Automatically calls get_conn() to establish connection.
97
98
Parameters:
99
- jira_conn_id: Connection ID reference for JIRA instance (default: 'jira_default')
100
- proxies: Optional proxy configuration for JIRA client
101
102
Attributes:
103
- jira_conn_id: Stored connection ID reference
104
- proxies: Stored proxy configuration
105
- client: JIRA client instance (initialized to None, set by get_conn())
106
107
Note: Constructor automatically calls get_conn() to initialize the client connection.
108
"""
109
110
def get_conn(self) -> JIRA:
111
"""
112
Get or create authenticated JIRA client connection.
113
Called automatically during initialization and whenever client is None.
114
115
Connection Configuration:
116
- Supports basic authentication via connection login/password
117
- Respects connection extra options: verify, validate, get_server_info
118
- Handles proxy configuration if provided during initialization
119
- Raises AirflowException if no jira_conn_id provided
120
121
Returns:
122
JIRA client instance ready for API operations
123
124
Raises:
125
AirflowException: If connection fails, no jira_conn_id provided, or authentication error occurs
126
JIRAError: Re-raised as AirflowException for JIRA-specific connection errors
127
"""
128
```
129
130
### JIRA Operations
131
132
Executes any JIRA Python SDK method through a generic operator interface, enabling comprehensive JIRA API access within Airflow workflows.
133
134
```python { .api }
135
class JiraOperator(BaseOperator):
136
template_fields = ("jira_method_args",)
137
138
@apply_defaults
139
def __init__(
140
self,
141
*,
142
jira_method: str,
143
jira_conn_id: str = 'jira_default',
144
jira_method_args: Optional[dict] = None,
145
result_processor: Optional[Callable] = None,
146
get_jira_resource_method: Optional[Callable] = None,
147
**kwargs,
148
) -> None:
149
"""
150
Generic operator for JIRA API operations using JIRA Python SDK.
151
Reference: http://jira.readthedocs.io
152
153
Parameters:
154
- jira_method: Method name from JIRA Python SDK to be called
155
- jira_conn_id: Connection ID reference for JIRA instance (default: 'jira_default')
156
- jira_method_args: Required method parameters for the jira_method (templated, default: None)
157
- result_processor: Optional function to further process the response from JIRA
158
- get_jira_resource_method: Optional function or operator to get jira resource on which the provided jira_method will be executed
159
160
Attributes:
161
- jira_conn_id: Stored connection ID reference
162
- method_name: Internal storage of jira_method parameter
163
- jira_method_args: Stored method arguments (supports templating)
164
- result_processor: Stored result processing function
165
- get_jira_resource_method: Stored resource method for advanced usage
166
"""
167
168
def execute(self, context: Dict) -> Any:
169
"""
170
Execute the configured JIRA method on the appropriate resource.
171
172
Execution Logic:
173
- If get_jira_resource_method is provided, executes jira_method on that resource
174
- If get_jira_resource_method is a JiraOperator, executes it to get the resource
175
- Otherwise executes jira_method on the top-level JIRA client
176
- Applies result_processor to the result if provided
177
178
Parameters:
179
- context: Airflow task execution context
180
181
Returns:
182
Result from JIRA method execution, optionally processed by result_processor
183
184
Raises:
185
AirflowException: If JIRA operation fails, method execution error occurs, or general error
186
JIRAError: JIRA-specific errors are caught and re-raised as AirflowException
187
188
Note: Current JIRA Python SDK (1.0.7) has issues with pickling JIRA responses for XCom.
189
"""
190
```
191
192
### JIRA Monitoring
193
194
Provides sensor capabilities for monitoring JIRA ticket states and field changes with customizable polling and timeout behavior.
195
196
```python { .api }
197
class JiraSensor(BaseSensorOperator):
198
@apply_defaults
199
def __init__(
200
self,
201
*,
202
method_name: str,
203
jira_conn_id: str = 'jira_default',
204
method_params: Optional[dict] = None,
205
result_processor: Optional[Callable] = None,
206
**kwargs,
207
) -> None:
208
"""
209
Generic sensor for monitoring JIRA state changes using jira-python-sdk methods.
210
Uses JiraOperator internally for JIRA API interactions.
211
212
Parameters:
213
- method_name: Method name from jira-python-sdk to be executed for monitoring
214
- jira_conn_id: Connection ID reference for JIRA instance (default: 'jira_default')
215
- method_params: Parameters for the method method_name (default: None)
216
- result_processor: Function that returns boolean and acts as sensor response (default: None)
217
218
Attributes:
219
- jira_conn_id: Stored connection ID reference
220
- result_processor: Initially set to None, then conditionally assigned from parameter
221
- method_name: Stored method name for JIRA API calls
222
- method_params: Stored method parameters
223
- jira_operator: Internal JiraOperator instance configured with sensor parameters
224
225
Initialization Logic:
226
- Sets result_processor to None first, then conditionally assigns if parameter provided
227
- Creates internal JiraOperator with task_id, connection, method, and parameters
228
"""
229
230
def poke(self, context: Dict) -> Any:
231
"""
232
Execute sensor check by delegating to internal JiraOperator.
233
234
Parameters:
235
- context: Airflow task execution context
236
237
Returns:
238
Result from internal jira_operator.execute() call, typically boolean for sensor evaluation
239
"""
240
```
241
242
### Ticket Field Monitoring
243
244
Specialized sensor for monitoring specific fields in JIRA tickets with built-in field comparison logic for common data types.
245
246
```python { .api }
247
class JiraTicketSensor(JiraSensor):
248
template_fields = ("ticket_id",)
249
250
@apply_defaults
251
def __init__(
252
self,
253
*,
254
jira_conn_id: str = 'jira_default',
255
ticket_id: Optional[str] = None,
256
field: Optional[str] = None,
257
expected_value: Optional[str] = None,
258
field_checker_func: Optional[Callable] = None,
259
**kwargs,
260
) -> None:
261
"""
262
Sensor for monitoring specific JIRA ticket field changes in terms of function.
263
264
Parameters:
265
- jira_conn_id: Connection ID reference for JIRA instance (default: 'jira_default')
266
- ticket_id: ID of the ticket to be monitored (templated, default: None)
267
- field: Field of the ticket to be monitored (default: None)
268
- expected_value: Expected value of the field (default: None)
269
- field_checker_func: Function that returns boolean and acts as sensor response (default: None, uses issue_field_checker)
270
271
Attributes:
272
- jira_conn_id: Stored connection ID reference
273
- ticket_id: Stored ticket ID for monitoring
274
- field: Stored field name to monitor
275
- expected_value: Stored expected field value
276
277
Initialization Logic:
278
- Sets instance attributes before calling parent constructor
279
- If field_checker_func is None, defaults to self.issue_field_checker
280
- Calls parent constructor with connection ID and result_processor
281
"""
282
283
def poke(self, context: Dict) -> Any:
284
"""
285
Check if ticket field matches expected value by dynamically configuring the internal operator.
286
287
Dynamic Configuration:
288
- Sets jira_operator.method_name to "issue"
289
- Sets jira_operator.jira_method_args to {'id': ticket_id, 'fields': field}
290
- Delegates to parent JiraSensor.poke() for execution
291
292
Parameters:
293
- context: Airflow task execution context
294
295
Returns:
296
Boolean indicating if field condition is met (from result_processor evaluation)
297
"""
298
299
def issue_field_checker(self, issue: Issue) -> Optional[bool]:
300
"""
301
Built-in field checker with type-specific comparison logic and error handling.
302
303
Field Type Handling:
304
- List fields: checks if expected_value is in the list
305
- String fields: case-insensitive string comparison
306
- Resource fields: case-insensitive comparison with resource.name attribute
307
- Other types: logs warning about unimplemented checker
308
309
Parameters:
310
- issue: JIRA Issue object to check
311
312
Returns:
313
Boolean result of field comparison, None if comparison cannot be performed
314
315
Error Handling:
316
- Catches JIRAError and logs as error
317
- Catches general exceptions and logs with details
318
- Returns None for any error conditions
319
320
Logging:
321
- Logs success when field matches expected value
322
- Logs info when field doesn't match expected value yet
323
"""
324
```
325
326
## Types
327
328
```python { .api }
329
# Core Python typing imports
330
from typing import Any, Callable, Dict, Optional
331
332
# JIRA Python SDK imports
333
from jira import JIRA
334
from jira.resources import Issue, Resource
335
from jira.exceptions import JIRAError
336
337
# Airflow core imports
338
from airflow.exceptions import AirflowException
339
from airflow.hooks.base_hook import BaseHook
340
from airflow.models import BaseOperator
341
from airflow.sensors.base_sensor_operator import BaseSensorOperator
342
from airflow.utils.decorators import apply_defaults
343
344
# Provider-specific imports
345
from airflow.providers.jira.hooks.jira import JiraHook
346
from airflow.providers.jira.operators.jira import JiraOperator
347
348
# Note: JIRAError is imported from jira.exceptions but also available through
349
# airflow.providers.jira.operators.jira for convenience (circular import pattern)
350
```
351
352
## Error Handling
353
354
All components convert JIRA-specific exceptions to Airflow exceptions for consistent error handling:
355
356
- **JIRAError**: JIRA Python SDK errors are caught and re-raised as AirflowException
357
- **Connection errors**: Authentication and network issues result in AirflowException
358
- **Method execution errors**: Invalid JIRA API method calls or parameter errors raise AirflowException
359
360
## Configuration
361
362
### Connection Setup
363
364
JIRA connections are configured through Airflow's connection management system:
365
366
```python
367
# Connection configuration supports:
368
# - Basic authentication (username/password)
369
# - SSL verification control
370
# - Server info validation
371
# - Proxy configuration
372
# - Custom JIRA client options via connection extras
373
```
374
375
### Templating Support
376
377
The following fields support Airflow templating for dynamic values:
378
379
- `JiraOperator.jira_method_args`: All method arguments can use templated values
380
- `JiraTicketSensor.ticket_id`: Ticket ID can be dynamically determined from context
381
382
## Common Usage Patterns
383
384
### Creating JIRA Issues
385
386
```python
387
create_issue = JiraOperator(
388
task_id='create_issue',
389
jira_method='create_issue',
390
jira_method_args={
391
'fields': {
392
'project': {'key': 'PROJ'},
393
'summary': 'Issue from Airflow',
394
'description': 'Automated issue creation',
395
'issuetype': {'name': 'Bug'},
396
'priority': {'name': 'High'},
397
}
398
}
399
)
400
```
401
402
### Updating JIRA Issues
403
404
```python
405
update_issue = JiraOperator(
406
task_id='update_issue',
407
jira_method='issue',
408
jira_method_args={'id': 'PROJ-123'},
409
get_jira_resource_method=lambda: hook.client.issue('PROJ-123').update(
410
summary='Updated summary',
411
description='Updated description'
412
)
413
)
414
```
415
416
### Waiting for Status Changes
417
418
```python
419
wait_for_resolved = JiraTicketSensor(
420
task_id='wait_resolved',
421
ticket_id='PROJ-123',
422
field='status',
423
expected_value='Resolved',
424
poke_interval=60,
425
timeout=1800,
426
)
427
```
428
429
### Custom Field Monitoring
430
431
```python
432
def custom_checker(context, jira_result):
433
issue = jira_result
434
return (issue.fields.assignee is not None and
435
issue.fields.priority.name == 'High')
436
437
wait_assigned_high_priority = JiraSensor(
438
task_id='wait_assigned_high',
439
method_name='issue',
440
method_params={'id': 'PROJ-123'},
441
result_processor=custom_checker,
442
)
443
```