Skip to content

Commit

Permalink
Merge pull request #22 from KevinDeJong-TomTom/feat/label-support
Browse files Browse the repository at this point in the history
feat: add support for array types string, component and version
  • Loading branch information
KevinDeJong-TomTom authored Feb 14, 2021
2 parents eb5f2d2 + c273fa0 commit 459b20d
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 37 deletions.
73 changes: 50 additions & 23 deletions jira_history_api/jira_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

from datetime import datetime
import logging
from typing import Callable

import atlassian

Expand Down Expand Up @@ -70,8 +69,14 @@ def _get_fields(self: object) -> dict:
_fields_dict[clause] = field

# Special cases
_fields_dict = utils.set_field_alias(_fields_dict, 'Fix Version', 'fixVersion')
_fields_dict = utils.set_field_alias(_fields_dict, 'Component', 'component')
aliases = {
'Fix Version': 'fixVersion',
'Version': 'affectedVersion',
'Component': 'component'
}

for alias, field in aliases.items():
_fields_dict = utils.set_field_alias(_fields_dict, alias, field)

return _fields_dict

Expand Down Expand Up @@ -110,8 +115,8 @@ def _get_version(self: object, project: str, version_id: str) -> dict:
"""
Retrieves the version associated with the given version ID
:param project: Project key associated with the version
:param version_d: Version ID associated with a version
:returns: Version when version ID is known or an empty dict otherwise
:param version_id: ID associated with a version
:returns: Version when `version_id` is known or an empty dict otherwise
"""
if not project or not version_id:
return {}
Expand All @@ -128,7 +133,14 @@ def _get_version(self: object, project: str, version_id: str) -> dict:
except KeyError:
logger.warning(f"Unknown version: {version_id}")

return {}

def _get_resolution(self: object, resolution_id: str) -> dict:
"""
Retrieves the resolution associated with the given resolution ID
:param resolution_id: ID associated with a resolution
:returns: Resolution when `resolution_id` is known or an empty dict otherwise
"""
if not resolution_id:
return {}

Expand All @@ -140,7 +152,14 @@ def _get_resolution(self: object, resolution_id: str) -> dict:
except KeyError:
logger.warning(f"Unknown resolution: {resolution_id}")

return {}

def _get_status(self: object, status_id: str) -> dict:
"""
Retrieves the status associated with the given status ID
:param status_id: ID associated with a status
:returns: Status when `status_id` is known or an empty dict otherwise
"""
if not status_id:
return {}

Expand All @@ -152,7 +171,14 @@ def _get_status(self: object, status_id: str) -> dict:
except KeyError:
logger.warning(f"Unknown status: {status_id}")

return {}

def _get_field(self: object, field: str) -> dict:
"""
Retrieves the full field description based on the name of the field
:param field: name of the field to retrieve
:returns: full field description if `field` is known or an empty dict otherwise
"""
if not field:
return {}

Expand All @@ -164,27 +190,28 @@ def _get_field(self: object, field: str) -> dict:
except KeyError:
logger.warning(f"Unknown field: {field}")

def _update_array_generic(self: object, update: dict, issue: dict, field: str, function: Callable):
_current = issue['fields'][field]
_project = issue['fields']['project']['key']

_from = function(_project, update['from'])
_to = function(_project, update['to'])

if not _from:
return [item for item in _current if item['id'] != _to['id']]
return {}

_current.append(_from)
return _current
def _update_array(self: object, field: dict, update: dict, issue: dict) -> dict:
"""
Update the provided issue based on the historical update of a field which is of
type `array`
:param field: Schema of the field to update
:param update: History item containing the update
:param issue: Issue to be updated
:returns: Updated issue
"""
_items = field['schema']['items']
if _items == 'version':
return utils.update_array_generic(issue, update, field['id'], self._get_version)

def _update_array(self: object, update: dict, issue: dict) -> dict:
if update['field'] == 'Fix Version':
return self._update_array_generic(update, issue, 'fixVersions', self._get_version)
if _items == 'component':
return utils.update_array_generic(issue, update, field['id'], self._get_component)

if update['field'] == 'Component':
return self._update_array_generic(update, issue, 'components', self._get_component)
if _items == 'string':
return update['fromString'].split(' ')

logger.error(f"Unsupport array type: {update['field']}")
logger.error(f"Unsupport array type: {update['field']} with schema {field}")
return {}

def _update_field(self: object, update: dict, issue: dict) -> dict:
Expand Down Expand Up @@ -213,7 +240,7 @@ def _update_field(self: object, update: dict, issue: dict) -> dict:
elif _field_type == 'user':
_value = self._get_user(update['from'])
elif _field_type == 'array':
_value = self._update_array(update, issue)
_value = self._update_array(field, update, issue)
elif _field_type == 'number':
_value = update['fromString']
else:
Expand Down
22 changes: 22 additions & 0 deletions jira_history_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,25 @@ def set_field_alias(data: dict, field: str, alias: str) -> dict:
logger.warning(f'The field "{alias}" is not a valid alias')

return data


def update_array_generic(issue: dict, update: dict, field: str, function: Callable):
"""
Updates an array based on a function which retrieves the full
data type.
:param issue: the issue which will be updated
:param update: the historical update
:param field: schema of the field to update
:param function: callable retrieving the full data type
"""
_current = issue['fields'][field]
_project = issue['fields']['project']['key']

_from = function(_project, update['from'])
_to = function(_project, update['to'])

if not _from:
return [item for item in _current if item['id'] != _to['id']]

_current.append(_from)
return _current
47 changes: 33 additions & 14 deletions test/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_get_no_field(self):
_field = {'id': 'customfield_00001', 'name': 'Test Field', 'clauseNames': ['customfield_00001']}
self.uut._jira.get_all_fields.return_value = [_field]

assert self.uut._get_field('invalid') is None
assert not self.uut._get_field('invalid')
self.uut._jira.get_all_fields.assert_called_once()

def test_get_invalid_field(self):
Expand Down Expand Up @@ -139,7 +139,7 @@ def test_get_invalid_version(self):
'projectId': 1
}]

assert self.uut._get_version(project='TEST', version_id=666) is None
assert not self.uut._get_version(project='TEST', version_id=666)
self.uut._jira.get_project_versions.assert_called_once()

def test_get_valid_version(self):
Expand Down Expand Up @@ -247,7 +247,7 @@ def test_get_no_resolution(self):
def test_get_invalid_resolution(self):
self.uut._jira.get_all_resolutions.return_value = None

assert self.uut._get_resolution(resolution_id='1') is None
assert not self.uut._get_resolution(resolution_id='1')
self.uut._jira.get_all_resolutions.assert_called_once()

def test_get_valid_resolution(self):
Expand Down Expand Up @@ -281,7 +281,7 @@ def test_get_no_status(self):
def test_get_invalid_status(self):
self.uut._jira.get_all_statuses.return_value = None

assert self.uut._get_status(status_id='1') is None
assert not self.uut._get_status(status_id='1')
self.uut._jira.get_all_statuses.assert_called_once()

def test_get_valid_status(self):
Expand Down Expand Up @@ -335,7 +335,8 @@ def setUp(self):
'id': '2'
},
'assignee': {'displayName': 'bill'},
'project': {'key': 'TEST'}
'project': {'key': 'TEST'},
'labels': ['old_label', 'new_label']
},
'changelog': {
'histories': []
Expand Down Expand Up @@ -410,7 +411,6 @@ def test_update_issue_multiple_dates(self):
assert _issue['fields']['summary'] == 'Uninteresting issue to solve'
assert _issue['fields']['dice'] == '6'


_issue = self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2018-11-30T09:00:00.000+0000'))
assert _issue['fields']['description'] == 'Fixed my typo'

Expand All @@ -421,8 +421,6 @@ def test_update_issue_multiple_dates(self):
_issue = self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2019-01-01T15:01:00.000+0000'))
assert _issue['fields']['dice'] == '3'



def test_update_issue_status_field(self):
self.uut._jira.get_all_fields.return_value = [{
'id': 'status',
Expand Down Expand Up @@ -500,12 +498,33 @@ def test_update_issue_user_field(self):
_issue = self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2018-06-01T09:01:00.000+0000'))
assert _issue['fields']['assignee']['displayName'] == 'bill'

def test_update_issue_fix_version(self):
def test_update_issue_array_string(self):
self.uut._jira.get_all_fields.return_value = [{
'clauseNames': ['labels'],
'id': 'labels',
'name': 'Labels',
'schema': {'items': 'string', 'system': 'labels', 'type': 'array'}}]

self.test_issue['changelog']['histories'] = [
{
'id': '1',
'created': '2018-06-01T09:00:00.000+0000',
'items': [{'field': 'labels', 'fieldtype': 'jira', 'from': '', 'fromString': 'old_label', 'to': '', 'toString': 'old_label new_label'}]
}
]

assert self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2018-06-01T09:01:00.000+0000')) == self.test_issue

_issue = self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2018-06-01T08:59:00.000+0000'))
assert len(_issue['fields']['labels']) == 1
assert _issue['fields']['labels'] == ['old_label']

def test_update_issue_array_version(self):
self.uut._jira.get_all_fields.return_value = [{
'id': 'fixVersion',
'name': 'Fix Version/s',
'clauseNames': ['fixVersion'],
'schema': {'type': 'array', 'items': 'version', 'system': 'fixVersion'}
'id': 'fixVersions',
'name': 'Fix Version/s',
'schema': {'items': 'version', 'system': 'fixVersions', 'type': 'array'}
}]

self.test_issue['changelog']['histories'] = [
Expand All @@ -523,8 +542,8 @@ def test_update_issue_fix_version(self):
assert self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2018-06-01T09:01:00.000+0000')) == self.test_issue

_issue = self.uut._update_issue_at_date(issue=copy.deepcopy(self.test_issue), date=utils.field_to_datetime('2018-06-01T08:59:00.000+0000'))
assert len(_issue['fields']['fixVersion']) == 1
assert _issue['fields']['fixVersion'][0]['name'] == '1.0.0'
assert len(_issue['fields']['fixVersions']) == 1
assert _issue['fields']['fixVersions'][0]['name'] == '1.0.0'


@contextlib.contextmanager
Expand Down

0 comments on commit 459b20d

Please sign in to comment.