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
203 changes: 194 additions & 9 deletions src/aaz_dev/cli/api/_cmds.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import click
import logging
from flask import Blueprint
import os
import sys

from cli.controller.az_module_manager import AzExtensionManager, AzMainManager
from command.controller.specs_manager import AAZSpecsManager
from command.controller.workspace_manager import WorkspaceManager
from command.model.configuration import CMDHelp
from swagger.controller.specs_manager import SwaggerSpecsManager
from swagger.model.specs import TypeSpecResourceProvider
from swagger.utils.source import SourceTypeEnum

from utils.config import Config
from utils.exceptions import InvalidAPIUsage

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')

bp = Blueprint('cli-cmds', __name__, url_prefix='/CLI/CMDs', cli_group="cli")
bp.cli.short_help = "Manage aaz commands in azure-cli and azure-cli-extensions."
Expand Down Expand Up @@ -39,9 +49,6 @@
help="Name of the module in azure-cli or the extension in azure-cli-extensions"
)
def regenerate_code(extension_or_module_name, cli_path=None, cli_extension_path=None):
from utils.config import Config
from utils.exceptions import InvalidAPIUsage
from cli.controller.az_module_manager import AzExtensionManager, AzMainManager
if not cli_path and not cli_extension_path:
logger.error("Please provide `--cli-path` or `--cli-extension-path`")
sys.exit(1)
Expand Down Expand Up @@ -129,11 +136,6 @@ def regenerate_code(extension_or_module_name, cli_path=None, cli_extension_path=
default=Config.CLI_DEFAULT_PROFILE,
)
def generate_by_swagger_tag(profile, swagger_tag, extension_or_module_name, cli_path=None, cli_extension_path=None):
from utils.config import Config
from utils.exceptions import InvalidAPIUsage
from cli.controller.az_module_manager import AzExtensionManager, AzMainManager
from swagger.controller.specs_manager import SwaggerSpecsManager
from command.controller.specs_manager import AAZSpecsManager
if not Config.DEFAULT_SWAGGER_MODULE:
Config.DEFAULT_SWAGGER_MODULE = "__MODULE__"

Expand Down Expand Up @@ -197,6 +199,189 @@ def generate_by_swagger_tag(profile, swagger_tag, extension_or_module_name, cli_
sys.exit(1)


@bp.cli.command("generate", short_help="Generate code effortlessly. If the result isn't what you expected, use the UI to fine-tune it.")
@click.option(
"--cli-path", '-c',
type=click.Path(file_okay=False, dir_okay=True, writable=True, readable=True, resolve_path=True),
callback=Config.validate_and_setup_cli_path,
help="The local path of azure-cli repo. Only required when generate code to azure-cli repo."
)
@click.option(
"--cli-extension-path", '-e',
type=click.Path(file_okay=False, dir_okay=True, writable=True, readable=True, resolve_path=True),
callback=Config.validate_and_setup_cli_extension_path,
help="The local path of azure-cli-extension repo. Only required when generate code to azure-cli-extension repo."
)
@click.option(
"--swagger-path", '-s',
type=click.Path(file_okay=False, dir_okay=True, readable=True, resolve_path=True),
default=Config.SWAGGER_PATH,
callback=Config.validate_and_setup_swagger_path,
expose_value=False,
help="The local path of azure-rest-api-specs repo. Official repo is https://github.com/Azure/azure-rest-api-specs"
)
@click.option(
"--aaz-path", '-a',
type=click.Path(file_okay=False, dir_okay=True, writable=True, readable=True, resolve_path=True),
default=Config.AAZ_PATH,
required=not Config.AAZ_PATH,
callback=Config.validate_and_setup_aaz_path,
expose_value=False,
help="The local path of aaz repo."
)
@click.option(
"--spec",
required=True,
help="Folder name of the specification source."
)
@click.option(
"--module",
required=True,
help="Module name of the CLI extension target."
)
def generate(spec, module, cli_path=None, cli_extension_path=None):
def _collect_resources(spec):
module_manager = SwaggerSpecsManager().get_module_manager(Config.DEFAULT_PLANE, spec)
rps = module_manager.get_resource_providers()

results = {}
for r in rps:
if isinstance(r, TypeSpecResourceProvider):
continue

rp = module_manager.get_openapi_resource_provider(r.name)
tag = r.default_tag

resource_map = rp.get_resource_map_by_tag(tag)
if not resource_map:
raise InvalidAPIUsage(f"Tag `{tag}` is not exist.")

results[rp.name] = (resource_map, tag)

return results

def _normalize_resource_map(resource_map, tag):
# covert resource_map to {version: [resources]}
version_resource_map = {}
for resource_id, version_map in resource_map.items():
v_list = list(version_map)
if len(v_list) > 1:
raise InvalidAPIUsage(
f"Tag `{tag}` contains multiple api versions of one resource",
payload={"Resource": resource_id, "versions": v_list},
)

v = v_list[0]
version_resource_map.setdefault(v, []).append({"id": resource_id})

return version_resource_map

def to_aaz():
results = _collect_resources(spec)

for rp_name, (resource_map, tag) in results.items():
version_resource_map = _normalize_resource_map(resource_map, tag)

ws = WorkspaceManager.new(
name=spec,
plane=Config.DEFAULT_PLANE,
folder=WorkspaceManager.IN_MEMORY,
mod_names=spec,
resource_provider=rp_name,
swagger_manager=SwaggerSpecsManager(),
aaz_manager=AAZSpecsManager(),
source=SourceTypeEnum.OpenAPI,
)
for version, resources in version_resource_map.items():
ws.add_new_resources_by_swagger(mod_names=spec, version=version, resources=resources)

# complete default help
for node in ws.iter_command_tree_nodes():
node.help = node.help or CMDHelp()
if not node.help.short:
node.help.short = f"Manage {node.names[-1]}."

for leaf in ws.iter_command_tree_leaves():
leaf.help = leaf.help or CMDHelp()
if not leaf.help.short:
n = leaf.names[-1]
leaf.help.short = f"{n.capitalize()} {leaf.names[-2]}"

cfg_editor = ws.load_cfg_editor_by_command(leaf)
command = cfg_editor.find_command(*leaf.names)
leaf.examples = ws.generate_examples_by_swagger(leaf, command)

if not ws.is_in_memory:
ws.save()

ws.generate_to_aaz()

def to_cli():
results = _collect_resources(spec)

commands_map = {}
for _, (resource_map, tag) in results.items():
for resource_id, version_map in resource_map.items():
v_list = list(version_map)
if len(v_list) > 1:
raise InvalidAPIUsage(
f"Tag `{tag}` contains multiple api versions of one resource",
payload={"Resource": resource_id, "versions": v_list},
)

v = v_list[0]
cfg_reader = AAZSpecsManager().load_resource_cfg_reader(Config.DEFAULT_PLANE, resource_id, v)
if not cfg_reader:
logger.error(f"Command models not exist in aaz for resource: {resource_id} version: {v}.")
continue

for cmd_names, command in cfg_reader.iter_commands():
key = tuple(cmd_names)
if key in commands_map and commands_map[key] != command.version:
raise ValueError(f"Multi version contained for command: {''.join(cmd_names)} versions: {commands_map[key]}, {command.version}")

commands_map[key] = command.version

if cli_path is not None:
assert Config.CLI_PATH is not None
manager = AzMainManager()

else: # generate ext module by default
assert Config.CLI_EXTENSION_PATH is not None
manager = AzExtensionManager()

if not manager.has_module(module):
logger.info(f"Create cli module `{module}`.")
manager.create_new_mod(module)

logger.info(f"Load cli module `{module}`.")
ext = manager.load_module(module)

profile = _build_profile(Config.CLI_DEFAULT_PROFILE, commands_map)
ext.profiles[profile.name] = profile

logger.info(f"Regenerate module `{module}`.")
manager.update_module(module, ext.profiles)

if cli_path and cli_extension_path:
logger.error("Please provide either `--cli-path` or `--cli-extension-path`.")
raise sys.exit(1)

spec_path = os.path.join(Config.SWAGGER_PATH, "specification", spec)
if not os.path.exists(spec_path) or not os.path.isdir(spec_path):
raise ValueError(f"Cannot find the specification name under {Config.SWAGGER_PATH}.")

try:
to_aaz()
logger.info("✔ Finished generating AAZ model.")
to_cli()
logger.info("✔ Finished generating AAZ codes.")

except (InvalidAPIUsage, ValueError) as err:
logger.error(err, exc_info=True)
raise sys.exit(1)


def _build_profile(profile_name, commands_map):
from cli.model.view import CLIViewProfile, CLIViewCommand, CLIViewCommandGroup
profile = CLIViewProfile({
Expand Down
6 changes: 3 additions & 3 deletions src/aaz_dev/cli/controller/az_atomic_profile_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from utils.plane import PlaneEnum
from utils.case import to_camel_case, to_snake_case

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class AzAtomicProfileBuilder:
Expand Down Expand Up @@ -248,8 +248,8 @@ def _complete_command_wait_info(cls, command_group):

for rid, value in [*wait_cmd_rids.items()]:
if "get_op" not in value:
logger.error(f'Failed to support wait command for resource: '
f'Get operation with provisioning state property does not exist: {rid}')
logger.warning(f'Failed to support wait command for resource: '
f'Get operation with provisioning state property does not exist: {rid}')
del wait_cmd_rids[rid]

if not wait_cmd_rids:
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/cli/controller/az_client_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from utils.plane import PlaneEnum
from utils.case import to_camel_case

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class AzClientsGenerator:
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/cli/controller/az_command_ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .az_arg_group_generator import AzArgClsGenerator
from .az_operation_generator import AzRequestClsGenerator, AzResponseClsGenerator

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class AzCommandCtx:
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/cli/controller/az_command_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .az_output_generator import AzOutputGenerator
from .az_selector_generator import AzJsonSelectorGenerator

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class AzCommandGenerator:
Expand Down
7 changes: 3 additions & 4 deletions src/aaz_dev/cli/controller/az_module_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from utils.config import Config
from collections import deque

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class AzModuleManager:
Expand All @@ -36,9 +36,8 @@ def get_aaz_path(self, mod_name):
raise NotImplementedError()

def has_module(self, mod_name):
mod_folder = self.get_mod_path(mod_name)
if not os.path.exists(mod_folder):
#print(f"Invalid module folder: cannot find modules in: '{mod_folder}'")
mod_file = os.path.join(self.get_mod_path(mod_name), "setup.py")
if not os.path.exists(mod_file):
return False
return True

Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/api/_cmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from swagger.utils.source import SourceTypeEnum
from utils.config import Config

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')

bp = Blueprint('aaz-cmds', __name__, url_prefix='/AAZ/CMDs', cli_group="command-model")
bp.cli.short_help = "Manage command models in aaz."
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/controller/specs_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from .cfg_validator import CfgValidator
from .command_tree import CMDSpecsPartialCommandTree

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class AAZSpecsManager:
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/controller/workspace_cfg_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .cfg_reader import CfgReader
from .workspace_helper import ArgumentUpdateMixin

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class WorkspaceCfgEditor(CfgReader, ArgumentUpdateMixin):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
CMDClientEndpointsByHttpOperation, CMDClientEndpoints,
CMDClientEndpointCloudMetadataTemplate)

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class WorkspaceClientCfgEditor(ClientCfgReader, ArgumentUpdateMixin):
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/controller/workspace_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from .workspace_cfg_editor import WorkspaceCfgEditor, build_endpoint_selector_for_client_config
from .workspace_client_cfg_editor import WorkspaceClientCfgEditor

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class WorkspaceManager:
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/model/configuration/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from ._resource import CMDResource


logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class CMDClientAADAuthConfig(Model):
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/model/configuration/_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from ._selector_index import CMDArrayIndexBase, CMDObjectIndexBase, CMDObjectIndexDiscriminator, CMDObjectIndexAdditionalProperties
from utils import exceptions

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class CMDCommand(Model):
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/model/configuration/_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging


logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class CMDBooleanField(BooleanType):
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/command/model/configuration/_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import logging
import re

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')


class CMDSchemaEnumItem(Model):
Expand Down
2 changes: 1 addition & 1 deletion src/aaz_dev/swagger/api/_cmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from utils.config import Config

logger = logging.getLogger('backend')
logger = logging.getLogger('aaz')

bp = Blueprint('swagger-cmds', __name__, url_prefix='/Swagger/CMDs', cli_group="swagger")
bp.cli.short_help = "Manage azure-rest-api-specs/azure-rest-api-specs-pr repos."
Expand Down
Loading
Loading