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

Adding wiki and wiki page commands for create, update, show, delete operations #428

Merged
merged 12 commits into from
Apr 3, 2019
5 changes: 5 additions & 0 deletions azure-devops/azext_devops/dev/common/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ def get_core_client(organization=None):
return connection.get_client(VSTS_MODULE + 'v5_0.core.core_client.CoreClient')


def get_wiki_client(organization=None):
connection = get_connection(organization)
return connection.get_client(VSTS_MODULE + 'v5_0.wiki.wiki_client.WikiClient')


def get_git_client(organization=None):
connection = get_connection(organization)
return connection.get_client(VSTS_MODULE + 'v5_0.git.git_client.GitClient')
Expand Down
38 changes: 38 additions & 0 deletions azure-devops/azext_devops/dev/team/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,40 @@ def _transform_team_row(row):
return table_row


def transform_wikis_table_output(result):
table_output = []
for item in sorted(result, key=_get_wiki_key):
table_output.append(_transform_wiki_row(item))
return table_output


def transform_wiki_table_output(result):
table_output = [_transform_wiki_row(result)]
return table_output


def _transform_wiki_row(row):
table_row = OrderedDict()
table_row['ID'] = row['id']
table_row['Name'] = row['name']
table_row['Type'] = row['type']
return table_row


def transform_wiki_page_table_output(result):
table_output = [_transform_wiki_page_row(result)]
return table_output


def _transform_wiki_page_row(row):
table_row = OrderedDict()
table_row['ETag'] = row['eTag']
table_row['Git Path'] = row['page']['gitItemPath']
table_row['Is Parent'] = row['page']['isParentPage']
table_row['order'] = row['page']['order']
return table_row


def transform_team_members_table_output(result):
table_output = []
for item in sorted(result, key=_get_member_key):
Expand Down Expand Up @@ -178,5 +212,9 @@ def _get_team_key(team_row):
return team_row['name'].lower()


def _get_wiki_key(wiki_row):
return wiki_row['name'].lower()


def _get_member_key(member_row):
return member_row['identity']['uniqueName'].lower()
10 changes: 10 additions & 0 deletions azure-devops/azext_devops/dev/team/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,13 @@ def load_team_help():
wikiIdentifier=e479f402-2be8-4953-bb0b-3a0209cbc2d2 --in-file D:/temp/createWikiRequestBody.txt
--query-parameters path=sample738 --http-method PUT --api-version 5.1-preview -o json
"""

helps['devops wiki'] = """
type: group
short-summary: Manage wikis
"""

helps['devops wiki page'] = """
type: group
short-summary: Manage wiki pages
"""
11 changes: 11 additions & 0 deletions azure-devops/azext_devops/dev/team/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# CUSTOM CHOICE LISTS
_YES_NO_SWITCH_VALUES = ['yes', 'no']
_SOURCE_CONTROL_VALUES = ['git', 'tfvc']
_WIKI_TYPE_VALUES = ['projectwiki', 'codewiki']
_PROJECT_VISIBILITY_VALUES = ['private', 'public']
_STATE_VALUES = ['invalid', 'unchanged', 'all', 'new', 'wellformed', 'deleting', 'createpending']
_SERVICE_ENDPOINT_TYPE = [SERVICE_ENDPOINT_TYPE_GITHUB, SERVICE_ENDPOINT_TYPE_AZURE_RM]
Expand All @@ -35,16 +36,22 @@ def load_global_args(context):
context.argument('project', options_list=('--project', '-p'), help='Name or ID of the project.')


# pylint: disable=too-many-statements
def load_team_arguments(self, _):
with self.argument_context('devops configure') as context:
context.argument('defaults', options_list=('--defaults', '-d'), nargs='*')

with self.argument_context('devops') as context:
context.argument('repository', options_list=('--repository', '-r'))

with self.argument_context('devops project') as context:
context.argument('process', options_list=('--process', '-p'))
context.argument('source_control', options_list=('--source-control', '-s'),
**enum_choice_list(_SOURCE_CONTROL_VALUES))
context.argument('description', options_list=('--description', '-d'))
context.argument('state', **enum_choice_list(_STATE_VALUES))
context.argument('visibility', **enum_choice_list(_PROJECT_VISIBILITY_VALUES))

with self.argument_context('devops service-endpoint create') as context:
context.argument('service_endpoint_type', **enum_choice_list(_SERVICE_ENDPOINT_TYPE))
context.argument('authorization_scheme', **enum_choice_list(_SERVICE_ENDPOINT_AUTHORIZATION_SCHEME))
Expand Down Expand Up @@ -106,3 +113,7 @@ def load_team_arguments(self, _):

with self.argument_context('pipelines') as context:
load_global_args(context)

with self.argument_context('devops wiki') as context:
context.argument('wiki_type', options_list=('--wiki-type', '--type'), **enum_choice_list(_WIKI_TYPE_VALUES))
context.argument('version', options_list=('--version', '-v'))
24 changes: 23 additions & 1 deletion azure-devops/azext_devops/dev/team/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
transform_user_table_output,
transform_extension_table_output,
transform_extensions_table_output,
transform_extension_search_results_table_output)
transform_extension_search_results_table_output,
transform_wiki_table_output,
transform_wikis_table_output,
transform_wiki_page_table_output)


projectOps = CliCommandType(
Expand Down Expand Up @@ -62,6 +65,11 @@
exception_handler=azure_devops_exception_handler
)

wikiOps = CliCommandType(
operations_tmpl='azext_devops.dev.team.wiki#{}',
exception_handler=azure_devops_exception_handler
)


def load_team_commands(self, _):
with self.command_group('devops', command_type=credentialsOps) as g:
Expand Down Expand Up @@ -111,3 +119,17 @@ def load_team_commands(self, _):
g.command('enable', 'enable_extension', table_transformer=transform_extension_table_output)
g.command('disable', 'disable_extension', table_transformer=transform_extension_table_output)
g.command('search', 'search_extensions', table_transformer=transform_extension_search_results_table_output)

with self.command_group('devops wiki', command_type=wikiOps) as g:
g.command('create', 'create_wiki', table_transformer=transform_wiki_table_output)
g.command('list', 'list_wiki', table_transformer=transform_wikis_table_output)
g.command('show', 'show_wiki', table_transformer=transform_wiki_table_output)
g.command('delete', 'delete_wiki', table_transformer=transform_wiki_table_output,
confirmation='Are you sure you want to delete this wiki?')

with self.command_group('devops wiki page', command_type=wikiOps) as g:
atbagga marked this conversation as resolved.
Show resolved Hide resolved
g.command('create', 'add_page', table_transformer=transform_wiki_page_table_output)
g.command('update', 'update_page', table_transformer=transform_wiki_page_table_output)
g.command('show', 'get_page', table_transformer=transform_wiki_page_table_output)
g.command('delete', 'delete_page',
confirmation='Are you sure you want to delete this wiki page?')
220 changes: 220 additions & 0 deletions azure-devops/azext_devops/dev/team/wiki.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import webbrowser
from knack.util import CLIError
from azext_devops.dev.common.services import (get_wiki_client,
get_core_client,
get_git_client,
resolve_instance,
resolve_instance_and_project,
resolve_instance_project_and_repo)


_DEFAULT_PAGE_ADD_MESSAGE = 'Added a new page using Azure DevOps CLI'
_DEFAULT_PAGE_UPDATE_MESSAGE = 'Updated the page using Azure DevOps CLI'
_DEFAULT_PAGE_DELETE_MESSAGE = 'Deleted the page using Azure DevOps CLI'


def create_wiki(name, wiki_type='projectwiki', mapped_path=None, version=None,
organization=None, project=None, repository=None, detect=None):
"""Create a wiki.
:param name: Name of the new wiki.
:type name: str
:param wiki_type: Type of wiki to create.
:type wiki_type: str
:param version: Repository branch name to publish the code wiki from. Only required for codewiki type.
:type version: str
:param mapped_path: Mapped path of the new wiki e.g. '/' to publish from root of repository.
Only required for codewiki type.
:type mapped_path: str
:param repository: Name or ID of the repository to publish the wiki from. Only required for codewiki type.
:type repository: str
"""
organization, project, repository = resolve_instance_project_and_repo(detect=detect,
organization=organization,
project=project,
repo=repository)
wiki_client = get_wiki_client(organization)
from azext_devops.devops_sdk.v5_0.wiki.models import WikiCreateParametersV2
wiki_params = WikiCreateParametersV2()
wiki_params.name = name
wiki_params.type = wiki_type
project_id = _get_project_id_from_name(organization=organization,
project=project)
wiki_params.project_id = project_id
repository_id = _get_repository_id_from_name(organization=organization,
project=project,
repository=repository)
wiki_params.repository_id = repository_id
if mapped_path:
wiki_params.mapped_path = mapped_path
if version:
from azext_devops.devops_sdk.v5_0.wiki.models import GitVersionDescriptor
version_descriptor = GitVersionDescriptor()
version_descriptor.version = version
wiki_params.version = version_descriptor
return wiki_client.create_wiki(wiki_create_params=wiki_params, project=project)


def delete_wiki(wiki, organization=None, project=None, detect=None):
"""Delete a wiki.
:param wiki: Name or Id of the wiki to delete.
:type wiki: str
"""
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
wiki_client = get_wiki_client(organization)
return wiki_client.delete_wiki(wiki_identifier=wiki, project=project)


def list_wiki(organization=None, project=None, detect=None):
"""List all the wikis in a project or organization.
"""
organization = resolve_instance(detect=detect,
organization=organization)
wiki_client = get_wiki_client(organization)
return wiki_client.get_all_wikis(project=project)


def show_wiki(wiki, open=False, organization=None, project=None, detect=None): # pylint: disable=redefined-builtin
"""Show details of a wiki.
:param wiki: Name or Id of the wiki.
:type wiki: str
:param open: Open the wiki in your web browser.
:type open: bool
"""
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
wiki_client = get_wiki_client(organization)
wiki_object = wiki_client.get_wiki(wiki_identifier=wiki, project=project)
if open:
webbrowser.open_new(url=wiki_object.remote_url)
return wiki_object


def add_page(wiki, path, comment=_DEFAULT_PAGE_ADD_MESSAGE, content=None, file_path=None,
organization=None, project=None, detect=None):
"""Add a new page.
:param wiki: Name or Id of the wiki.
:type wiki: str
:param path: Path of the wiki page.
:type path: str
:param content: Content of the wiki page. Ignored if --file-path is specified.
:type content: str
:param file_path: Path of the file input if content is specified in the file.
:type file_path: str
:param comment: Comment in the commit message of file add operation.
:type comment: str
"""
if not content and not file_path:
atbagga marked this conversation as resolved.
Show resolved Hide resolved
raise CLIError('Either --file-path or --content must be specified.')
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
wiki_client = get_wiki_client(organization)
from azext_devops.devops_sdk.v5_0.wiki.models import WikiPageCreateOrUpdateParameters
parameters = WikiPageCreateOrUpdateParameters()
if content:
parameters.content = content
if file_path:
fp = open(file_path, mode='r')
parameters.content = fp.read()
fp.close()
return wiki_client.create_or_update_page(parameters=parameters, wiki_identifier=wiki,
project=project, path=path, version=None, comment=comment)


def update_page(wiki, path, comment=_DEFAULT_PAGE_UPDATE_MESSAGE, content=None, file_path=None,
version=None, organization=None, project=None, detect=None):
"""Edit a page.
:param wiki: Name or Id of the wiki.
:type wiki: str
:param path: Path of the wiki page.
:type path: str
:param content: Content of the wiki page. Ignored if --file-path is specified.
:type content: str
:param file_path: Path of the file input if content is specified in the file.
:type file_path: str
:param comment: Comment in the commit message of file edit operation.
:type comment: str
:param version: Version (ETag) of file to edit.
:type version: str
"""
if not content and not file_path:
raise CLIError('Either --file-path or --content must be specified.')
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
wiki_client = get_wiki_client(organization)
from azext_devops.devops_sdk.v5_0.wiki.models import WikiPageCreateOrUpdateParameters
parameters = WikiPageCreateOrUpdateParameters()
if content:
parameters.content = content
if file_path:
fp = open(file_path, mode='r')
parameters.content = fp.read()
fp.close()
return wiki_client.create_or_update_page(parameters=parameters, wiki_identifier=wiki,
project=project, path=path, version=version, comment=comment)


def get_page(wiki, path, version=None, recursion_level=None, open=False, # pylint: disable=redefined-builtin
include_content=False, organization=None, project=None, detect=None):
"""Get the content of a page or open a page.
:param wiki: Name or Id of the wiki.
:type wiki: str
:param path: Path of the wiki page.
:type path: str
:param version: Version (ETag) of the wiki page.
:type version: str
:param recursion_level: Recursion level of the wiki page.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit confused, this is get_page or get_pages 🤣 (due to recursion_level)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also I believe recursion_level has some fixed set of values allowed (please check once) and we can make this an enum

Copy link
Collaborator Author

@atbagga atbagga Apr 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API is same. This is just the wrapped up API so let us discuss this in bash and close how we want the get pages to be exposed.

:type recursion_level: str
:param include_content: Include content of the page.
:type include_content: str
:param open: Open the wiki page in your web browser.
:type open: bool
"""
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
wiki_client = get_wiki_client(organization)
page_object = wiki_client.get_page(
wiki_identifier=wiki, project=project, path=path,
recursion_level=recursion_level, version_descriptor=version,
include_content=include_content)
if open:
webbrowser.open_new(url=page_object.page.remote_url)
return page_object


def delete_page(wiki, path, comment=_DEFAULT_PAGE_DELETE_MESSAGE, organization=None, project=None, detect=None):
"""Delete a page.
:param wiki: Name or Id of the wiki.
:type wiki: str
:param path: Path of the wiki page.
:type path: str
:param comment: Comment in the commit message of delete operation.
:type comment: str
"""
organization, project = resolve_instance_and_project(detect=detect,
organization=organization,
project=project)
wiki_client = get_wiki_client(organization)
return wiki_client.delete_page(wiki_identifier=wiki, path=path, comment=comment, project=project)


def _get_project_id_from_name(organization, project):
core_client = get_core_client(organization)
team_project = core_client.get_project(project_id=project)
return team_project.id


def _get_repository_id_from_name(organization, project, repository):
git_client = get_git_client(organization)
repository = git_client.get_repository(project=project, repository_id=repository)
return repository.id