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

Add "Did You Mean This" Extension #1692

Merged
merged 36 commits into from
May 15, 2020
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8305ee4
Setup extension scaffolding, create dummy version command, add help/h…
Apr 4, 2020
c29b090
Add v0 recommendation file. Handle general extension logic for when r…
Apr 4, 2020
ce8e6de
Fix empty iterable check, add guard against extension usage.
Apr 6, 2020
2476efd
Disable linter warning for CLIError import, add more robust relative …
Apr 6, 2020
3cb8aac
Add lookup table. Adjust minimum CLI version. Comment out parameter l…
Apr 8, 2020
e7916b0
Move "not able to help" message to function, accept list of parameter…
Apr 13, 2020
89ccf9c
Fix typo in comment.
Apr 13, 2020
3871f0a
Remove comment.
Apr 14, 2020
0aaa994
Clean up extension logic, adjust style based on flake8 and pylint.
Apr 14, 2020
f708094
Integration tests, async/sync cli version check, integrate service in…
Apr 28, 2020
f6b5d69
Don't capture NumberOfPairs, register command table callback, disable…
Apr 29, 2020
9bf6db3
Normalize parameters and remove invalid command tokens where possible…
May 3, 2020
408a261
Update tests to reflect current recommendation model. Update recordi…
May 5, 2020
d0f65c0
Switch to mock aladdin service call data, rename files, add mock reco…
May 6, 2020
6fc9cd4
Add copyright notice to all files, update minCliCoreVersion to 2.4.0,…
May 7, 2020
ab8dfa3
Assert that the correct command is returned from the normalize_and_so…
May 7, 2020
28d0140
Add additional comments/clarification.
May 7, 2020
edd4f86
Drop Python 2 support from setup.cfg
May 7, 2020
55aa449
Merge branch 'master' into thoth-extension
jiasli May 8, 2020
bee938b
Update to use standard library mock, disable colorama import error fo…
May 8, 2020
9005692
Make extension test more generic.
May 8, 2020
cf00711
Patch the correct setup.cfg file.
May 11, 2020
162a2ef
Remove sensitive file, merge command group, handle exception from req…
May 13, 2020
955a1fc
Handle case in which the correlation ID or subscription ID is not set.
May 13, 2020
0a3eb00
Don't use get_subscription_id.
May 13, 2020
1c806ad
Remove erroneous import.
May 13, 2020
d0819db
Remove another erroneous import.
May 13, 2020
c1eb46b
Add instructions for trying out an experimental release of the extens…
May 13, 2020
144ff52
Added a detailed README, fixed bug in which invalid tokens were not r…
May 14, 2020
e4b790c
Merge remote-tracking branch 'origin/master' into thoth-extension
May 14, 2020
9982777
Add final newline to _const.py
May 14, 2020
665e3fd
Make styling check obey coloring configuration in core.
May 14, 2020
79ee91b
Update src/ai-did-you-mean-this/setup.py
May 14, 2020
1569869
Update src/ai-did-you-mean-this/setup.py
May 14, 2020
f28c256
Changed variable name to avoid chromium issue 981129.
May 14, 2020
d9ea28e
Merge branch 'thoth-extension' of https://github.com/christopher-o-to…
May 14, 2020
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
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,6 @@

/src/kusto/ @ilayr @orhasban @astauben

/src/ai-did-you-mean-this/ @christopher-o-toole

/src/custom-providers/ @jsntcy
10 changes: 10 additions & 0 deletions src/ai-did-you-mean-this/HISTORY.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. :changelog:

Release History
===============

0.1.0
++++++
* Initial release.
* Add autogenerated recommendations for recovery from UserFault failures.
* Ensure that the hook is activated in common UserFault failure scenarios.
91 changes: 91 additions & 0 deletions src/ai-did-you-mean-this/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Microsoft Azure CLI 'AI Did You Mean This' Extension #

### Installation ###
To install the extension, use the below CLI command
```
az extension add --name ai-did-you-mean-this
```

### Background ###
The purpose of this extension is help users recover from failure. Once installed, failure recovery recommendations will automatically be provided for supported command failures. In cases where no recommendations are available, a prompt to use `az find` will be shown provided that the command can be matched to a valid CLI command.
### Limitations ###
For now, recommendations are limited to parser failures (i.e. not in a command group, argument required, unrecognized parameter, expected one argument, etc). Support for more core failures is planned for a future release.
### Try it out! ###
The following examples demonstrate how to trigger the extension. For a more complete list of supported CLI failure types, see this [CLI PR](https://github.com/Azure/azure-cli/pull/12889). Keep in mind that the recommendations shown here may be different from the ones that you receive.

#### az account ####
```
> az account
az account: error: the following arguments are required: _subcommand
usage: az account [-h]
{list,set,show,clear,list-locations,get-access-token,lock,management-group}
...

Here are the most common ways users succeeded after [account] failed:
az account list
az account show
```

#### az account set ####
```
> az account set
az account set: error: the following arguments are required: --subscription/-s
usage: az account set [-h] [--verbose] [--debug] [--only-show-errors]
[--output {json,jsonc,yaml,yamlc,table,tsv,none}]
[--query JMESPATH] --subscription SUBSCRIPTION

Here are the most common ways users succeeded after [account set] failed:
az account set --subscription Subscription
```

#### az group create ####
```
>az group create
az group create: error: the following arguments are required: --name/--resource-group/-n/-g, --location/-l
usage: az group create [-h] [--verbose] [--debug] [--only-show-errors]
[--output {json,jsonc,yaml,yamlc,table,tsv,none}]
[--query JMESPATH] [--subscription _SUBSCRIPTION]
--name RG_NAME --location LOCATION
[--tags [TAGS [TAGS ...]]] [--managed-by MANAGED_BY]

Here are the most common ways users succeeded after [group create] failed:
az group create --location westeurope --resource-group MyResourceGroup
```
#### az vm list ###
```
> az vm list --query ".id"
az vm list: error: argument --query: invalid jmespath_type value: '.id'
usage: az vm list [-h] [--verbose] [--debug] [--only-show-errors]
[--output {json,jsonc,yaml,yamlc,table,tsv,none}]
[--query JMESPATH] [--subscription _SUBSCRIPTION]
[--resource-group RESOURCE_GROUP_NAME] [--show-details]

Sorry I am not able to help with [vm list]
Try running [az find "az vm list"] to see examples of [vm list] from other users.
```
### Developer Build ###
If you want to try an experimental release of the extension, it is recommended you do so in a [Docker container](https://www.docker.com/resources/what-container). Keep in mind that you'll need to install Docker and pull the desired [Azure CLI image](https://hub.docker.com/_/microsoft-azure-cli) from the Microsoft Container Registry before proceeding.

#### Setting up your Docker Image ####
To run the Azure CLI Docker image as an interactive shell, run the below command by replacing `<version>` with your desired CLI version
```bash
docker run -it mcr.microsoft.com/azure-cli:<version>
export EXT="ai-did-you-mean-this"
pip install --upgrade --target ~/.azure/cliextensions/$EXT "git+https://github.com/christopher-o-toole/azure-cli-extensions.git@thoth-extension#subdirectory=src/$EXT&egg=$EXT"
```
Each time you start a new shell, you'll need to login before you can start using the extension. To do so, run
```bash
az login
```
and follow the instructions given in the prompt. Once you're logged in, try out the extension by issuing a faulty command
```
> az account
az account: error: the following arguments are required: _subcommand
usage: az account [-h]
{list,set,show,clear,list-locations,get-access-token,lock,management-group}
...

Here are the most common ways users succeeded after [account] failed:
az account list
az account show
```
52 changes: 52 additions & 0 deletions src/ai-did-you-mean-this/azext_ai_did_you_mean_this/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader

from knack.events import (
EVENT_INVOKER_CMD_TBL_LOADED
)

from azext_ai_did_you_mean_this._help import helps # pylint: disable=unused-import
from azext_ai_did_you_mean_this._cmd_table import on_command_table_loaded


def inject_functions_into_core():
from azure.cli.core.parser import AzCliCommandParser
from azext_ai_did_you_mean_this.custom import recommend_recovery_options
AzCliCommandParser.recommendation_provider = recommend_recovery_options


# pylint: disable=too-few-public-methods
class GlobalConfig():
ENABLE_STYLING = False


class AiDidYouMeanThisCommandsLoader(AzCommandsLoader):
def __init__(self, cli_ctx=None):
from azure.cli.core.commands import CliCommandType

ai_did_you_mean_this_custom = CliCommandType(
operations_tmpl='azext_ai_did_you_mean_this.custom#{}')
super().__init__(cli_ctx=cli_ctx,
custom_command_type=ai_did_you_mean_this_custom)
self.cli_ctx.register_event(EVENT_INVOKER_CMD_TBL_LOADED, on_command_table_loaded)
inject_functions_into_core()
# per https://github.com/Azure/azure-cli/pull/12601
try:
GlobalConfig.ENABLE_STYLING = cli_ctx.enable_color
except AttributeError:
pass

def load_command_table(self, args):
from azext_ai_did_you_mean_this.commands import load_command_table
load_command_table(self, args)
return self.command_table

def load_arguments(self, command):
pass


COMMAND_LOADER_CLS = AiDidYouMeanThisCommandsLoader
13 changes: 13 additions & 0 deletions src/ai-did-you-mean-this/azext_ai_did_you_mean_this/_cmd_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------


class CommandTable(): # pylint: disable=too-few-public-methods
CMD_TBL = None


def on_command_table_loaded(_, **kwargs):
cmd_tbl = kwargs.pop('cmd_tbl', None)
CommandTable.CMD_TBL = cmd_tbl
34 changes: 34 additions & 0 deletions src/ai-did-you-mean-this/azext_ai_did_you_mean_this/_const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

UPDATE_RECOMMENDATION_STR = (
"Better failure recovery recommendations are available from the latest version of the CLI. "
"Please update for the best experience.\n"
)

UNABLE_TO_HELP_FMT_STR = (
'\nSorry I am not able to help with [{command}]'
'\nTry running [az find "az {command}"] to see examples of [{command}] from other users.'
)

RECOMMENDATION_HEADER_FMT_STR = (
'\nHere are the most common ways users succeeded after [{command}] failed:'
)

TELEMETRY_MUST_BE_ENABLED_STR = (
'User must agree to telemetry before failure recovery recommendations can be presented.'
)

TELEMETRY_MISSING_SUBSCRIPTION_ID_STR = (
"Subscription ID was not set in telemetry."
)

TELEMETRY_MISSING_CORRELATION_ID_STR = (
"Correlation ID was not set in telemetry."
)

UNABLE_TO_CALL_SERVICE_STR = (
'Either the subscription ID or correlation ID was not set. Aborting operation.'
)
17 changes: 17 additions & 0 deletions src/ai-did-you-mean-this/azext_ai_did_you_mean_this/_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# coding=utf-8
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from knack.help_files import helps # pylint: disable=unused-import

helps['ai-did-you-mean-this'] = """
type: group
short-summary: Add recommendations for recovering from failure.
"""

helps['ai-did-you-mean-this version'] = """
type: command
short-summary: Prints the extension version.
"""
28 changes: 28 additions & 0 deletions src/ai-did-you-mean-this/azext_ai_did_you_mean_this/_style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import sys

from azext_ai_did_you_mean_this import GlobalConfig


def style_message(msg):
if should_enable_styling():
import colorama # pylint: disable=import-error

try:
msg = colorama.Style.BRIGHT + msg + colorama.Style.RESET_ALL
except KeyError:
pass
return msg


def should_enable_styling():
try:
if GlobalConfig.ENABLE_STYLING and sys.stdout.isatty():
return True
except AttributeError:
pass
return False
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"azext.isPreview": true,
"azext.minCliCoreVersion": "2.4.0"
}
12 changes: 12 additions & 0 deletions src/ai-did-you-mean-this/azext_ai_did_you_mean_this/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=line-too-long


def load_command_table(self, _):

with self.command_group('ai-did-you-mean-this', is_preview=True) as g:
g.custom_command('version', 'show_extension_version')
Loading