Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boards Iterations and Areas Unit tests and Backlog/Default team iteration commands #663

Merged
merged 24 commits into from
Jun 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4fde12c
Fix python versions in Mac machines
ishitam8 Apr 12, 2019
528804e
Merge branch 'master' of https://github.com/Azure/azure-devops-cli-ex…
ishitam8 Apr 16, 2019
59825dc
Merge branch 'master' of https://github.com/Azure/azure-devops-cli-ex…
ishitam8 May 23, 2019
ada2ef6
Merge branch 'master' of https://github.com/ishitam8/azure-devops-cli…
ishitam8 May 27, 2019
c297877
Merge branch 'master' of https://github.com/Azure/azure-devops-cli-ex…
ishitam8 May 27, 2019
344fbe7
Merge branch 'master' of https://github.com/Azure/azure-devops-cli-ex…
ishitam8 Jun 4, 2019
b846daa
UTs for Project iterations
ishitam8 Jun 4, 2019
a8ca98a
UTs
ishitam8 Jun 4, 2019
ee5b304
UTs for Project Area commands
ishitam8 Jun 6, 2019
dcc52d0
UTs for Team area commands
ishitam8 Jun 6, 2019
1ee1cf1
Unwanted import
ishitam8 Jun 6, 2019
068af76
Bug bash fix: Change from relative to absolute path
ishitam8 Jun 7, 2019
2a60248
Absolute path in iterations
ishitam8 Jun 7, 2019
877dd0e
Add commands to configure default iteration and backlog iteration
ishitam8 Jun 10, 2019
4e14365
Change help text for update commands
ishitam8 Jun 10, 2019
9227b3d
Minor help text changes
ishitam8 Jun 11, 2019
daf1b75
Changes in default area
ishitam8 Jun 11, 2019
608fa1d
Hanlde empty backlog iteration ID error and adding a troubleshotting …
ishitam8 Jun 12, 2019
58674be
Fix UTs
ishitam8 Jun 12, 2019
1c1bbd5
Merge branch 'master' of https://github.com/Azure/azure-devops-cli-ex…
ishitam8 Jun 12, 2019
5b0d9ea
Pylint fixes
ishitam8 Jun 13, 2019
e2e2674
Flake fixes
ishitam8 Jun 13, 2019
1b4b9d6
Additional tests for Backlog/default and list work items command
ishitam8 Jun 13, 2019
eb9af77
Renaming test helper file
ishitam8 Jun 13, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions azure-devops/azext_devops/dev/boards/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,54 @@ def _transform_team_iteration_row(row):
table_row = OrderedDict()
table_row['ID'] = row['id']
table_row['Name'] = row['name']
if row['attributes']['startDate'] is None:
table_row['Start Date'] = ''
else:
table_row['Start Date'] = row['attributes']['startDate']
if row['attributes']['finishDate'] is None:
table_row['Finish Date'] = ''
else:
table_row['Finish Date'] = row['attributes']['finishDate']
if row['attributes']:
if row['attributes']['startDate'] is None:
table_row['Start Date'] = ''
else:
table_row['Start Date'] = row['attributes']['startDate']
if row['attributes']['finishDate'] is None:
table_row['Finish Date'] = ''
else:
table_row['Finish Date'] = row['attributes']['finishDate']
if 'timeFrame' in row['attributes']:
table_row['Time Frame'] = row['attributes']['timeFrame']
table_row['Path'] = row['path']
return table_row


def transform_work_item_team_iteration_work_items(result):
table_output = []
for item in result['workItemRelations']:
table_output.append(_transform_team_iteration_work_item_row(item))
return table_output


def _transform_team_iteration_work_item_row(row):
table_row = OrderedDict()
if row['source']:
table_row['Source'] = row['source']['id']
if row['target']:
table_row['Target'] = row['target']['id']
table_row['Relation Type'] = row['rel']
return table_row


def transform_work_item_team_default_iteration_table_output(result):
table_output = []
table_row = OrderedDict()
if result['defaultIteration']:
table_row = _transform_team_iteration_row(result['defaultIteration'])
table_row['Default Iteration Macro'] = result['defaultIterationMacro']
table_output.append(table_row)
return table_output


def transform_work_item_team_backlog_iteration_table_output(result):
table_output = []
table_output.append(_transform_team_iteration_row(result['backlogIteration']))
return table_output


def transform_work_item_project_classification_nodes_table_output(response):
table_op = []
table_op = transform_work_item_project_classification_nodes_table_output_recursive(response, table_op)
Expand Down
27 changes: 27 additions & 0 deletions azure-devops/azext_devops/dev/boards/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ def load_boards_help():
long-summary:
"""

helps['boards iteration project update'] = """
type: command
long-summary: Move iteration or update iteration details like name AND/OR start-date and finish-date.
"""

helps['boards area'] = """
type: group
short-summary: Manage area paths.
Expand All @@ -55,12 +60,34 @@ def load_boards_help():
long-summary:
"""

helps['boards area project update'] = """
type: command
long-summary: Move area or update area name.
"""

helps['boards area team'] = """
type: group
short-summary: Manage areas for a team.
long-summary:
"""

helps['boards area team update'] = """
type: command
long-summary: Update any area to include/exclude sub areas OR Set already added area as default.
"""

helps['boards area team add'] = """
type: command
long-summary: Every team needs to have a default area configured which can't be empty.
Hence, you need to pass --set-as-default while adding first area to your team.
You can later configure any other area which already added to team as default
by using `az boards area team update -h` command.
examples:
- name: Add area to a team.
text: |
az boards area team --team 'ContosoTeam' --path '\\ContosoProject\\MyProjectAreaName'
"""

helps['boards work-item relation'] = """
type: group
short-summary: Manage work item relations.
Expand Down
25 changes: 17 additions & 8 deletions azure-devops/azext_devops/dev/boards/area.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@
from azext_devops.dev.common.services import (resolve_instance_and_project,
get_work_item_tracking_client,
get_work_client)

from .boards_helper import resolve_classification_node_path
_STRUCTURE_GROUP_AREA = 'areas'


def get_project_areas(depth=1, path=None, organization=None, project=None, detect=None):
"""List areas for a project.
:param depth: Depth of child nodes to be fetched.
:param depth: Depth of child nodes to be fetched. Example: --depth 3
:type depth: int
"""
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
client = get_work_item_tracking_client(organization)
if path:
path = resolve_classification_node_path(client, path, project, _STRUCTURE_GROUP_AREA)
list_of_areas = client.get_classification_node(project=project,
structure_group=_STRUCTURE_GROUP_AREA,
depth=depth, path=path)
Expand All @@ -37,6 +39,7 @@ def delete_project_area(path, organization=None, project=None, detect=None):
organization=organization,
project=project)
client = get_work_item_tracking_client(organization)
path = resolve_classification_node_path(client, path, project, _STRUCTURE_GROUP_AREA)
response = client.delete_classification_node(project=project,
structure_group=_STRUCTURE_GROUP_AREA,
path=path)
Expand All @@ -52,6 +55,8 @@ def create_project_area(name, path=None, organization=None, project=None, detect
organization=organization,
project=project)
client = get_work_item_tracking_client(organization)
if path:
path = resolve_classification_node_path(client, path, project, _STRUCTURE_GROUP_AREA)
classification_node_object = WorkItemClassificationNode()
classification_node_object.name = name
response = client.create_or_update_classification_node(project=project,
Expand All @@ -76,8 +81,8 @@ def get_project_area(id, organization=None, project=None, detect=None): # pylin
return response


def update_project_area(path=None, name=None, child_id=None, organization=None, project=None, detect=None):
"""Move area or update area name.
def update_project_area(path, name=None, child_id=None, organization=None, project=None, detect=None):
"""Update area.
:param name: New name of the area.
:type: str
:param child_id: Move an existing area and add as child node for this area.
Expand All @@ -89,6 +94,7 @@ def update_project_area(path=None, name=None, child_id=None, organization=None,
organization=organization,
project=project)
client = get_work_item_tracking_client(organization)
path = resolve_classification_node_path(client, path, project, _STRUCTURE_GROUP_AREA)
if child_id:
move_classification_node_object = WorkItemClassificationNode()
move_classification_node_object.id = child_id
Expand Down Expand Up @@ -123,14 +129,13 @@ def get_team_areas(team, organization=None, project=None, detect=None):
def add_team_area(path, team, set_as_default=False, include_sub_areas=None,
organization=None, project=None, detect=None):
"""Add area to a team.
:param set_as_default: Set this area path as default area for this team.
:param set_as_default: Set this area path as default area for this team. Default: False
:type set_as_default: bool
"""
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
client = get_work_client(organization)

team_context = TeamContext(project=project, team=team)
get_response = client.get_team_field_values(team_context=team_context)
patch_doc = TeamFieldValuesPatch()
Expand Down Expand Up @@ -159,9 +164,13 @@ def remove_team_area(path, team, organization=None, project=None, detect=None):
if get_response.default_value == path:
raise CLIError('You are trying to remove the default area for this team. '
'Please change the default area node and then try this command again.')
area_found = False
for entry in get_response.values:
if path == entry.value[:]:
area_found = True
get_response.values.remove(entry)
if not area_found:
raise CLIError('Path is not added to team area list.')
patch_doc = TeamFieldValuesPatch()
patch_doc.values = get_response.values
patch_doc.default_value = get_response.default_value
Expand All @@ -171,8 +180,8 @@ def remove_team_area(path, team, organization=None, project=None, detect=None):

def update_team_area(path, team, include_sub_areas=None, set_as_default=False,
organization=None, project=None, detect=None):
"""Update any area to include/exclude sub areas OR Set already added area as default.
:param default_area:set_as_default: Set as default team area path.
"""Update team area.
:param set_as_default: Set as default team area path. Default: False
:type set_as_default: bool
"""
if include_sub_areas is None and set_as_default is False:
Expand Down
14 changes: 10 additions & 4 deletions azure-devops/azext_devops/dev/boards/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def load_work_arguments(self, _):
Multiple values can be passed comma separated. Example: 1,2 ')

with self.argument_context('boards iteration project') as context:
context.argument('path', help='Iteration path.')
context.argument('path', help='Absolute path of an iteration. '
'Example:' + r'\ProjectName\Iteration\IterationName')
context.argument('start_date',
help='Start date of the iteration. Example : "2019-06-03"')
context.argument('finish_date',
Expand All @@ -46,15 +47,20 @@ def load_work_arguments(self, _):
context.argument('id', type=int)

with self.argument_context('boards iteration project create') as context:
context.argument('path', help='Iteration path. Creates an iteration at root level if --path is not specified.')
context.argument('path', help='Absolute path of an iteration. '
'Creates an iteration at root level if --path is not specified. '
'Example:' + r'\ProjectName\Iteration\IterationName.')

with self.argument_context('boards area') as context:
context.argument('path', help='Area path.')
context.argument('path', help='Absolute path of an area. Example:' + r'\ProjectName\Area\AreaName')

with self.argument_context('boards area project create') as context:
context.argument('path', help='Area path. Creates an area at root level if --path is not specified.')
context.argument('path', help='Absolute path of an area. '
'Creates an area at root level if --path is not specified. '
'Example:' + r'\ProjectName\Area\AreaName.')

with self.argument_context('boards area team') as context:
context.argument('team', help='The name or id of the team.')
context.argument('include_sub_areas', arg_type=get_three_state_flag(),
help='Include child nodes of this area.')
context.argument('path', help='Area path. Example:' + r'\ProjectName\AreaName')
18 changes: 18 additions & 0 deletions azure-devops/azext_devops/dev/boards/boards_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from knack.util import CLIError


def resolve_classification_node_path(client, path, project, structure_group):
get_root_node = client.get_root_nodes(project=project, depth=0)
root_node_path = None
for entry in get_root_node:
if entry.structure_type == structure_group[:-1]:
root_node_path = entry.additional_properties['path']
if root_node_path and path.lower().startswith(root_node_path.lower()):
updated_path = path[len(root_node_path):]
return updated_path
raise CLIError("--path parameter is expected to be absolute path.")
16 changes: 14 additions & 2 deletions azure-devops/azext_devops/dev/boards/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
transform_work_item_relations,
transform_work_item_team_iterations_table_output,
transform_work_item_team_iteration_table_output,
transform_work_item_team_iteration_work_items,
transform_work_item_team_default_iteration_table_output,
transform_work_item_team_backlog_iteration_table_output,
transform_work_item_project_classification_nodes_table_output,
transform_work_item_project_classification_node_table_output,
transform_work_item_team_areas_table_output)
Expand Down Expand Up @@ -62,7 +65,16 @@ def load_work_commands(self, _):
with self.command_group('boards iteration team', command_type=workProjectAndTeamIterationOps) as g:
# team iteration commands
g.command('list', 'get_team_iterations', table_transformer=transform_work_item_team_iterations_table_output)
g.command('show', 'get_team_iteration', table_transformer=transform_work_item_team_iteration_table_output)
g.command('list-work-items', 'list_iteration_work_items',
table_transformer=transform_work_item_team_iteration_work_items)
g.command('set-default-iteration', 'set_default_iteration',
table_transformer=transform_work_item_team_default_iteration_table_output)
g.command('set-backlog-iteration', 'set_backlog_iteration',
table_transformer=transform_work_item_team_backlog_iteration_table_output)
g.command('show-default-iteration', 'show_default_iteration',
table_transformer=transform_work_item_team_default_iteration_table_output)
g.command('show-backlog-iteration', 'show_backlog_iteration',
table_transformer=transform_work_item_team_backlog_iteration_table_output)
g.command('remove', 'delete_team_iteration', table_transformer=transform_work_item_team_iteration_table_output)
g.command('add', 'post_team_iteration', table_transformer=transform_work_item_team_iteration_table_output)

Expand All @@ -75,7 +87,7 @@ def load_work_commands(self, _):
g.command('delete', 'delete_project_iteration',
confirmation='Are you sure you want to delete this iteration?')
g.command('show', 'get_project_iteration',
table_transformer=transform_work_item_project_classification_nodes_table_output)
table_transformer=transform_work_item_project_classification_node_table_output)
g.command('create', 'create_project_iteration',
table_transformer=transform_work_item_project_classification_nodes_table_output)

Expand Down
Loading