azure-devops/azext_devops/dev/boards/relations.py (99 lines of code) (raw):
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from knack.log import get_logger
from knack.util import CLIError
from azext_devops.devops_sdk.v5_0.work_item_tracking.models import JsonPatchOperation, Wiql
from azext_devops.dev.common.services import (get_work_item_tracking_client,
resolve_instance)
logger = get_logger(__name__)
def get_relation_types_show(organization=None, detect=None):
""" List work item relations supported in the organization.
"""
organization = resolve_instance(detect=detect, organization=organization)
client = get_work_item_tracking_client(organization)
return client.get_relation_types()
def add_relation(id, relation_type, target_id=None, target_url=None, organization=None, detect=None): # pylint: disable=redefined-builtin
""" Add relation(s) to work item.
"""
if target_id is None and target_url is None:
raise CLIError('--target-id or --target-url must be provided')
organization = resolve_instance(detect=detect, organization=organization)
patch_document = []
client = get_work_item_tracking_client(organization)
relation_types_from_service = client.get_relation_types()
relation_type_system_name = get_system_relation_name(relation_types_from_service, relation_type)
patch_document = []
if target_id is not None:
target_work_item_ids = target_id.split(',')
work_item_query_clause = []
for target_work_item_id in target_work_item_ids:
work_item_query_clause.append('[System.Id] = {}'.format(target_work_item_id))
wiql_query_format = 'SELECT [System.Id] FROM WorkItems WHERE ({})'
wiql_query_to_get_target_work_items = wiql_query_format.format(' OR '.join(work_item_query_clause))
wiql_object = Wiql()
wiql_object.query = wiql_query_to_get_target_work_items
target_work_items = client.query_by_wiql(wiql=wiql_object).work_items
if len(target_work_items) != len(target_work_item_ids):
raise CLIError('Id(s) supplied in --target-id is not valid')
for target_work_item in target_work_items:
op = _create_patch_operation('add', '/relations/-', relation_type_system_name, target_work_item.url)
patch_document.append(op)
if target_url is not None:
target_urls = target_url.split(',')
for url in target_urls:
op = _create_patch_operation('add', '/relations/-', relation_type_system_name, url)
patch_document.append(op)
client.update_work_item(document=patch_document, id=id)
work_item = client.get_work_item(id, expand='All')
work_item = fill_friendly_name_for_relations_in_work_item(relation_types_from_service, work_item)
return work_item
def remove_relation(id, relation_type, target_id, organization=None, detect=None): # pylint: disable=redefined-builtin
""" Remove relation(s) from work item.
"""
organization = resolve_instance(detect=detect, organization=organization)
patch_document = []
client = get_work_item_tracking_client(organization)
relation_types_from_service = client.get_relation_types()
relation_type_system_name = get_system_relation_name(relation_types_from_service, relation_type)
target_work_item_ids = target_id.split(',')
main_work_item = client.get_work_item(id, expand='All')
if main_work_item.relations:
for target_work_item_id in target_work_item_ids:
target_work_item = client.get_work_item(target_work_item_id, expand='All')
target_work_item_url = target_work_item.url
index = 0
for relation in main_work_item.relations:
if relation.rel == relation_type_system_name and relation.url == target_work_item_url:
po = _create_patch_operation('remove', '/relations/{}'.format(index))
patch_document.append(po)
break
index = index + 1
if len(patch_document) != len(target_work_item_ids):
raise CLIError('Id(s) supplied in --target-id is not valid')
client.update_work_item(document=patch_document, id=id)
work_item = client.get_work_item(id, expand='All')
work_item = fill_friendly_name_for_relations_in_work_item(relation_types_from_service, work_item)
return work_item
def show_work_item(id, organization=None, detect=None): # pylint: disable=redefined-builtin
""" Get work item, fill relations with friendly name
"""
organization = resolve_instance(detect=detect, organization=organization)
client = get_work_item_tracking_client(organization)
work_item = client.get_work_item(id, expand='All')
relation_types_from_service = client.get_relation_types()
work_item = fill_friendly_name_for_relations_in_work_item(relation_types_from_service, work_item)
return work_item
def fill_friendly_name_for_relations_in_work_item(relation_types_from_service, wi):
if not wi.relations:
return wi
for relation in wi.relations:
for relation_type_from_service in relation_types_from_service:
if relation_type_from_service.reference_name == relation.rel:
relation.rel = relation_type_from_service.name
return wi
def get_system_relation_name(relation_types_from_service, relation_type):
for relation_type_from_service in relation_types_from_service:
if relation_type_from_service.name.lower() == relation_type.lower():
return relation_type_from_service.reference_name
raise CLIError("--relation-type is not valid. Use \"az boards work-item relation list-type\" " +
"command to list possible relation types in your project")
def _create_patch_operation(op, path, rel=None, url=None):
patch_operation = JsonPatchOperation()
patch_operation.op = op
patch_operation.path = path
if rel is not None and url is not None:
patch_operation.value = {
'rel': rel,
'url': url
}
return patch_operation