Skip to content

yandex/yandex_tracker_client

Repository files navigation

yandex_tracker_client

yandex_tracker_client provides python interface to Yandex Tracker v2 API.

Installation

yandex_tracker_client could be installed by pip:

pip install yandex_tracker_client

Configuring

The Yandex Tracker API uses the OAuth 2.0 protocol for application authorization. Applications use the OAuth 2.0 protocol to access Yandex services on behalf of a user. You can get your token here. You can also use IAM tokens for Yandex Cloud organizations. Read here about IAM tokens.

Usage

To use client you need to initialize client first:

from yandex_tracker_client import TrackerClient

client = TrackerClient(token=<token>, org_id=<org_id>)

For Yandex Cloud organizations you need to use cloud_org_id instead of org_id parameter. Use iam_token parameter for temporary IAM tokens.

Getting issue:

issue = client.issues['MYQUEUE-42']
print issue.deadline, issue.updatedAt

Handling 404 exceptions:

from yandex_tracker_client.exceptions import NotFound

try:
    issue = client.issues['MYQUEUE-42']
except NotFound:
    pass

Creating issue: Full field list here. .. code:: python

client.issues.create(
queue='MYQUEUE', summary='API Test Issue', type={'name': 'Bug'}, description='test description'

)

Updating issue:

issue = client.issues['MYQUEUE-42']
issue.update(summary='East or west, Yandex Tracker is the best', priority='minor')

Searching for issues:

issues = client.issues.find('Queue: MYQUEUE Assignee: me()') #You can copy this query from ui tracker interface
print [issue.key for issue in issues]

Using the 'filter' parameter possible to pass the parameters of the filtering as a dictionary:

issues = client.issues.find(
    filter={'queue': 'MYQUEUE', 'assignee': 'me()', 'created': {'from': '2019-03-02'}},
    order=['update','-status', '+priority'],
    per_page=15
)
print [issue.key for issue in issues]

Obtaining list of transitions:

transitions = issue.transitions.get_all()
for transition in transitions:
  print transition

Executing transition:

issue = client.issues['MYQUEUE-42']
issue.transitions['close'].execute()

Executing transition with comment and resolution:

issue = client.issues['MYQUEUE-42']
transition = issue.transitions['close']
transition.execute(comment='Fixed', resolution='fixed')

Queue info:

queue = client.queues['MYQUEUE']

or:

queue = client.issues['MYQUEUE-42'].queue

Queue list:

queues = client.queues.get_all()[:3]

List issue attachments:

attachments = client.issues['MYQUEUE-42'].attachments

Downloading attachments to specified directory:

[attachment.download_to('some/path') for attachments in client.get_attachments('MYQUEUE-42')]

Uploading an attachment

issue = client.issues['MYQUEUE-42']
issue.attachments.create('path/to/file')

Deleting an attachment

ATTACHMENTS_TO_DELETE = {'to_delete.txt', 'smth.jpeg'}
issue = client.issues['MYQUEUE-42']
for attach in issue.attachments:
    if attach.name in ATTACHMENTS_TO_DELETE:
        attach.delete()

or

issue.attachments[42].delete()

List issue comments:

issue = client.issues['MYQUEUE-42']
comments = list(issue.comments.get_all())[:3]

Add comment:

issue = client.issues['MYQUEUE-42']
comment = issue.comments.create(text='Test Comment')

Add comment with attachments:

issue = client.issues['MYQUEUE-42']
comment = issue.comments.create(text='Test comment', attachments=['path/to/file1', 'path/to/file2'])

Update comment:

issue = client.issues['MYQUEUE-42']
comment = issue.comments[42]
comment.update(text='New Text')

Deleting a comment:

issue = client.issues['MYQUEUE-42']
comment = issue.comments[42]
comment.delete()

List issue links:

issue = client.issues['MYQUEUE-42']
links = issue.links

Add link:

issue = client.issues['MYQUEUE-42']
link = issue.links.create(issue='TEST-42', relationship='relates')

Deleting a link:

issue = client.issues['MYQUEUE-42']
link = issue.links[42]
link.delete()

Add remote link:

issue = client.issues['MYQUEUE-42']
link = issue.remotelinks.create(origin="ru.yandex.lunapark", key="MYQUEUE-42", relationship="relates")

Advanced Usage

Bulk update:

bulkchange = client.bulkchange.update(
    ['MYQUEUE-42', 'MYQUEUE-43', 'MYQUEUE-44'],
    priority='minor',
    tags={'add': ['minored']})
print bulkchange.status
bulkchange = bulkchange.wait()
print bulkchange.status

Bulk transition:

bulkchange = client.bulkchange.transition(
    ['MYQUEUE-42', 'MYQUEUE-43'], 'need_info', priority='minor')
bulkchange.wait()

Bulk move:

bulkchange = client.bulkchange.move(['MYQUEUE-42', 'MYQUEUE-43'], 'TEST')
bulkchange.wait()

Perform actions with objects

Client allows to make arbitrary subqueries to entities, for example in order to archive version you have to make request POST /v2/versions/<id>/_archive

In order to support such separate subqueries exists method perform_action, usage example:

version = client.versions[60031]
version.perform_action('_archive', 'post', ignore_empty_body=True)

Some of tracker api endpoints doesn't work correctly with blank ({}) body, in this case you should pass ignore_empty_body=True to this method. If endpoint require list in body use list_data param and just pass needed kwargs otherwise.

Examples

Change assignee in all tickets

from yandex_tracker_client import TrackerClient

client = TrackerClient(token=<token>, org_id=<org_id>)

def sent_employee_to_vacation(assignee, replace_with):
    """
    :param assignee: login in Yandex Tracker
    :type assignee: ``str``

    :param replace_with: login in Yandex Tracker
    :type replace_with: ``str``

    :return: is operation was successful
    :rtype: ``bool``
    """
    issues_to_transfer = client.issues.find(filter={'queue': 'MYQUEUE', 'assignee': assignee})
    bulk_change = client.bulkchange.update(issues_to_transfer, assignee=replace_with)
    bulk_change.wait()

    if bulk_change.status == 'COMPLETED':
        log.info('Successfully change assignee in bulkchange {}'.format(bulk_change.id))
        for issue in issues_to_transfer:
            issue.comments.create('Your ticket will be processed by another employee - {}'.format(replace_with))
        successful = True
    else:
        log.error('Bulkchange operation {} failed'.format(bulk_change.id))
        successful = False

    return successful

Create related issues

def start_new_feature_creation_process(feature):
    feature_type = get_feature_type(feature)
    manager = get_manager_by_type(feature_type)
    # manager = 'manager_login'
    main_issue = client.issues.create(
        queue='MAINQUEUE',
        assignee=manager,
        summary='New feature request: {}'.format(feature),
        type={'name': 'Task'},
        description='New feature request arrived'
    )
    if feature_type.need_design:
        design_issue = client.issues.create(
            queue='DESIGN',
            summary='Feature "{}" design'.format(feature),
            type={'name': 'Task'},
            description='Need design for new feature, main task: {}'.format(main_issue.id)
        )
        main_issue.links.create(issue=design_issue.id, relationship='relates')

    if feature_type.add_followers:
        followers = get_followers(feature_type)
        # followers = ['my_login', 'someoneelse_login']
        main_issue.update(followers={'add': followers})

    if feature_type.need_testing:
        tester = get_random_tester()
        # tester = 'tester_login'
        main_issue.update(qa=tester)

    log.info('Successfully start new feature creation process')
    return main_issue.id

Run tests

./run_tests.sh