Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion src/azure-cli-core/azure/cli/core/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def _print_examples(help_file):
from colorama import Style
indent = 0
_print_indent('Examples', indent)
for e in help_file.examples:
for e in AzCliHelp.example_provider(help_file):
indent = 1
_print_indent(u'{0}'.format(e.short_summary), indent)
indent = 2
Expand Down Expand Up @@ -188,6 +188,11 @@ def update_loaders_with_help_file_contents(self, nouns):
file_contents[name] = self._name_to_content[name]
self.versioned_loaders[ldr_cls_name].update_file_contents(file_contents)

# This method is meant to be a hook that can be overridden by an extension or module.
@staticmethod
def example_provider(help_file):
return help_file.examples
Comment on lines +193 to +194
Copy link
Member

@jiasli jiasli Jan 9, 2020

Choose a reason for hiding this comment

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

Does example_provider rely on help_file.examples? In other words, is example_provider appending the az find examples to the manually-written examples by itself? If not, I would prefer only passing help_file.command to reduce coupling.

    def _print_examples(help_file):
        ...
        # AzCliHelp.example_provider can append more examples to help_file.example 
        generated_example = AzCliHelp.example_provider(help_file.command)
        help_file.examples.extend(generated_example)
        for e in help_file.examples:
    def example_provider(command):
        return []

If so, the code is good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The ai-examples extension needs both the command that's being looked up and the examples that command has so it can prevent any duplicate examples from being added by the Aladdin service. help_file has both of those values, so I'd like to keep the current usage of it.

It also gives the extension the possibility of changing the order of all the examples. For instance, we could do something like organize them by usage (to be clear: it's not something we're planning right now, but could be useful in the future).



class CliHelpFile(KnackHelpFile):

Expand Down
46 changes: 32 additions & 14 deletions src/azure-cli/azure/cli/command_modules/find/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# --------------------------------------------------------------------------------------------
from __future__ import print_function

from collections import namedtuple
import random
import json
import re
Expand All @@ -24,12 +25,13 @@
EXTENSION_NAME = 'find'


Example = namedtuple("Example", "title snippet")


def process_query(cli_term):
if not cli_term:
logger.error('Please provide a search term e.g. az find "vm"')
else:
response = call_aladdin_service(cli_term)

print(random.choice(WAIT_MESSAGE), file=sys.stderr)
response = call_aladdin_service(cli_term)

Expand All @@ -50,23 +52,25 @@ def process_query(cli_term):
print("\nHere are the most common ways to use [" + cli_term + "]: \n", file=sys.stderr)

for answer in answer_list:
current_title = answer['title'].strip()
current_snippet = answer['snippet'].strip()
if current_title.startswith("az "):
current_title, current_snippet = current_snippet, current_title
current_title = current_title.split('\r\n')[0]
elif '```azurecli\r\n' in current_snippet:
start_index = current_snippet.index('```azurecli\r\n') + len('```azurecli\r\n')
current_snippet = current_snippet[start_index:]
current_snippet = current_snippet.replace('```', '').replace(current_title, '').strip()
current_snippet = re.sub(r'\[.*\]', '', current_snippet).strip()
print(style_message(current_title))
print(current_snippet + '\n')
cleaned_answer = clean_from_http_answer(answer)
print(style_message(cleaned_answer.title))
print(cleaned_answer.snippet + '\n')
if has_pruned_answer:
print(style_message("More commands and examples are available in the latest version of the CLI. "
"Please update for the best experience.\n"))


def get_generated_examples(cli_term):
examples = []
response = call_aladdin_service(cli_term)

if response.status_code == 200:
for answer in json.loads(response.content):
examples.append(clean_from_http_answer(answer))

return examples


def style_message(msg):
if should_enable_styling():
try:
Expand Down Expand Up @@ -118,3 +122,17 @@ def call_aladdin_service(query):
headers=headers)

return response


def clean_from_http_answer(http_answer):
current_title = http_answer['title'].strip()
current_snippet = http_answer['snippet'].strip()
if current_title.startswith("az "):
current_title, current_snippet = current_snippet, current_title
current_title = current_title.split('\r\n')[0]
elif '```azurecli\r\n' in current_snippet:
start_index = current_snippet.index('```azurecli\r\n') + len('```azurecli\r\n')
current_snippet = current_snippet[start_index:]
current_snippet = current_snippet.replace('```', '').replace(current_title, '').strip()
current_snippet = re.sub(r'\[.*\]', '', current_snippet).strip()
return Example(current_title, current_snippet)
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,79 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import contextlib
import json
import unittest
import mock
import sys
import six
from six import StringIO
import requests

from azure.cli.command_modules.find.custom import call_aladdin_service
from azure.cli.core.mock import DummyCli
from azure.cli.command_modules.find.custom import (Example, call_aladdin_service,
get_generated_examples, clean_from_http_answer)


def create_valid_http_response():
mock_response = requests.Response()
mock_response.status_code = 200
data = [{
'title': 'RunTestAutomation',
'snippet': 'az find'
}, {
'title': 'az test',
'snippet': 'The title'
}]
mock_response._content = json.dumps(data)
return mock_response


def create_empty_http_response():
mock_response = requests.Response()
mock_response.status_code = 200
data = []
mock_response._content = json.dumps(data)
return mock_response


class FindCustomCommandTest(unittest.TestCase):

def test_call_aladdin_service(self):
response = call_aladdin_service("RunTestAutomation")
self.assertEqual(200, response.status_code)
mock_response = create_valid_http_response()

with mock.patch('requests.get', return_value=(mock_response)):
response = call_aladdin_service('RunTestAutomation')
self.assertEqual(200, response.status_code)
self.assertEqual(2, len(json.loads(response.content)))

def test_example_clean_from_http_answer(self):
cleaned_responses = []
mock_response = create_valid_http_response()

for response in json.loads(mock_response.content):
cleaned_responses.append(clean_from_http_answer(response))

self.assertEqual('RunTestAutomation', cleaned_responses[0].title)
self.assertEqual('az find', cleaned_responses[0].snippet)
self.assertEqual('The title', cleaned_responses[1].title)
self.assertEqual('az test', cleaned_responses[1].snippet)

def test_get_generated_examples_full(self):
examples = []
mock_response = create_valid_http_response()

with mock.patch('requests.get', return_value=(mock_response)):
examples = get_generated_examples('RunTestAutomation')

self.assertEqual('RunTestAutomation', examples[0].title)
self.assertEqual('az find', examples[0].snippet)
self.assertEqual('The title', examples[1].title)
self.assertEqual('az test', examples[1].snippet)

def test_get_generated_examples_empty(self):
examples = []
mock_response = create_empty_http_response()

with mock.patch('requests.get', return_value=(mock_response)):
examples = get_generated_examples('RunTestAutomation')

self.assertEqual(0, len(examples))


if __name__ == '__main__':
Expand Down