mirror of
https://github.com/valitydev/redash.git
synced 2024-11-07 09:28:51 +00:00
Merge pull request #1298 from getredash/add_jql
Add: JIRA (JQL) query runner
This commit is contained in:
commit
808fdd4507
@ -63,7 +63,7 @@ Graphite
|
||||
|
||||
- **Options**:
|
||||
|
||||
- Url (mandatory)
|
||||
- URL (mandatory)
|
||||
- User
|
||||
- Password
|
||||
- Verify SSL certificate
|
||||
@ -270,3 +270,15 @@ Microsoft SQL Server
|
||||
|
||||
- ``freetds-dev`` C library
|
||||
- ``pymssql`` python package, requires FreeTDS to be installed first
|
||||
|
||||
|
||||
JIRA (JQL)
|
||||
----------
|
||||
|
||||
- **Options**:
|
||||
|
||||
- URL (your JIRA instance url)
|
||||
- Username
|
||||
- Password
|
||||
|
||||
For information on how to write JIRA/JQL queries, see :doc:`documentation </usage/jira_querying>`.
|
||||
|
35
docs/usage/jira_querying.rst
Normal file
35
docs/usage/jira_querying.rst
Normal file
@ -0,0 +1,35 @@
|
||||
JIRA (JQL): Querying
|
||||
#################
|
||||
|
||||
*Simple query, just return issues with no filtering:*
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
*Return only specific fields:*
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"fields": "summary,priority"
|
||||
}
|
||||
|
||||
*Return only specific fields and filter by priority:*
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"fields": "summary,priority",
|
||||
"jql": "priority=medium"
|
||||
}
|
||||
|
||||
*Count number of issues with `priority=medium`:*
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"queryType": "count",
|
||||
"jql": "priority=medium"
|
||||
}
|
135
redash/query_runner/jql.py
Normal file
135
redash/query_runner/jql.py
Normal file
@ -0,0 +1,135 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from redash.query_runner import *
|
||||
|
||||
|
||||
# TODO: make this more general and move into __init__.py
|
||||
class ResultSet(object):
|
||||
def __init__(self):
|
||||
self.columns = OrderedDict()
|
||||
self.rows = []
|
||||
|
||||
def add_row(self, row):
|
||||
for key in row.keys():
|
||||
self.add_column(key)
|
||||
|
||||
self.rows.append(row)
|
||||
|
||||
def add_column(self, column, column_type=TYPE_STRING):
|
||||
if column not in self.columns:
|
||||
self.columns[column] = {'name': column, 'type': column_type, 'friendly_name': column}
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps({'rows': self.rows, 'columns': self.columns.values()})
|
||||
|
||||
|
||||
def parse_issue(issue):
|
||||
result = OrderedDict()
|
||||
result['key'] = issue['key']
|
||||
|
||||
for k, v in issue['fields'].iteritems():
|
||||
if k.startswith('customfield_'):
|
||||
continue
|
||||
|
||||
if isinstance(v, dict):
|
||||
if 'key' in v:
|
||||
result['{}_key'.format(k)] = v['key']
|
||||
if 'name' in v:
|
||||
result['{}_name'.format(k)] = v['name']
|
||||
|
||||
if k in v:
|
||||
result[k] = v[k]
|
||||
|
||||
if 'watchCount' in v:
|
||||
result[k] = v['watchCount']
|
||||
# elif isinstance(v, list):
|
||||
# pass
|
||||
else:
|
||||
result[k] = v
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def parse_issues(data):
|
||||
results = ResultSet()
|
||||
|
||||
for issue in data['issues']:
|
||||
results.add_row(parse_issue(issue))
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def parse_count(data):
|
||||
results = ResultSet()
|
||||
results.add_row({'count': data['total']})
|
||||
return results
|
||||
|
||||
|
||||
class JiraJQL(BaseQueryRunner):
|
||||
@classmethod
|
||||
def configuration_schema(cls):
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'title': 'JIRA URL'
|
||||
},
|
||||
'username': {
|
||||
'type': 'string',
|
||||
},
|
||||
'password': {
|
||||
'type': 'string'
|
||||
}
|
||||
},
|
||||
'required': ['url', 'username', 'password'],
|
||||
'secret': ['password']
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def name(cls):
|
||||
return "JIRA (JQL)"
|
||||
|
||||
@classmethod
|
||||
def annotate_query(cls):
|
||||
return False
|
||||
|
||||
def __init__(self, configuration):
|
||||
super(JiraJQL, self).__init__(configuration)
|
||||
self.syntax = 'json'
|
||||
|
||||
def run_query(self, query_string):
|
||||
jql_url = '{}/rest/api/2/search'.format(self.configuration["url"])
|
||||
|
||||
try:
|
||||
query = json.loads(query_string)
|
||||
query_type = query.pop('queryType', 'select')
|
||||
|
||||
if query_type == 'count':
|
||||
query['maxResults'] = 1
|
||||
query['fields'] = ''
|
||||
|
||||
response = requests.get(jql_url, params=query, auth=(self.configuration.get('username'), self.configuration.get('password')))
|
||||
|
||||
if response.status_code == 401 or response.status_code == 403:
|
||||
return None, "Authentication error. Please check username/password."
|
||||
|
||||
if response.status_code != 200:
|
||||
return None, "JIRA returned unexpected status code ({})".format(response.status_code)
|
||||
|
||||
data = response.json()
|
||||
|
||||
if query_type == 'count':
|
||||
results = parse_count(data)
|
||||
else:
|
||||
results = parse_issues(data)
|
||||
|
||||
return results.to_json(), None
|
||||
except KeyboardInterrupt:
|
||||
return None, "Query cancelled by user."
|
||||
|
||||
register(JiraJQL)
|
||||
|
Loading…
Reference in New Issue
Block a user