modular_cli/modular_cli.py (95 lines of code) (raw):
import sys
from json import JSONDecodeError
from http import HTTPStatus
import click
from modular_cli.service.decorators import (
dynamic_dispatcher, CommandResponse, ResponseDecorator,
)
from modular_cli.service.help_client import (
retrieve_commands_meta_content, HelpProcessor,
)
from modular_cli.service.config import ConfigurationProvider, add_data_to_config, CONF_ACCESS_TOKEN, CONF_REFRESH_TOKEN
from modular_cli.service.initializer import init_configuration
from modular_cli.service.request_processor import prepare_request
from modular_cli.service.utils import find_token_meta
from modular_cli.utils.exceptions import ModularCliInternalException
from modular_cli.service.utils import JWTToken
from modular_cli.utils.logger import get_logger
from modular_cli.utils.variables import NO_CONTENT_RESPONSE_MESSAGE
from modular_cli.service.adapter_client import AdapterClient
CONTEXT_SETTINGS = dict(allow_extra_args=True, ignore_unknown_options=True)
# if you are going to change the value of the next line - please change
# correspond value in Modular-API
_LOG = get_logger(__name__)
@click.command(context_settings=CONTEXT_SETTINGS)
@click.pass_context
@click.option('--help', is_flag=True, default=False)
@click.option('--json', is_flag=True, default=False)
@click.option('--table', is_flag=True, default=False)
@ResponseDecorator(click.echo, 'Response is broken.')
@dynamic_dispatcher
def modular_cli(
command: list | None = None,
parameters: list | None = None,
help: bool = False,
view_type: str | None = None,
) -> CommandResponse:
commands_meta = retrieve_commands_meta_content()
token_meta = find_token_meta(
commands_meta=commands_meta, specified_tokens=command,
)
is_help = __is_help_required(
token_meta=token_meta, specified_parameters=parameters, help_flag=help,
)
if is_help:
help_processor = HelpProcessor(
requested_command=command, commands_meta=commands_meta,
)
help_message = help_processor.get_help_message(token_meta=token_meta)
click.echo(help_message)
sys.exit(0)
resource, method, parameters, params_to_log = prepare_request(
token_meta=token_meta, passed_parameters=parameters,
)
adapter_sdk = handle_token_expiration(init_configuration())
response = adapter_sdk.execute_command(
resource=resource, parameters=parameters,
method=method, params_to_log=params_to_log)
if response.status_code == HTTPStatus.NO_CONTENT.value:
return CommandResponse(message=NO_CONTENT_RESPONSE_MESSAGE)
try:
response_body = response.json()
except JSONDecodeError:
return CommandResponse(
message='Can not parse response into json. Please check logs',
code=int(HTTPStatus.BAD_REQUEST),
)
except Exception:
raise ModularCliInternalException(
'Unexpected error happened. Please contact the Maestro support team'
)
return CommandResponse(**response_body, code=response.status_code)
def __is_help_required(token_meta, specified_parameters, help_flag):
if help_flag:
return help_flag
if not token_meta.get('route'):
return True
required_parameters = [
param for param in token_meta.get('parameters') if param.get('required')
]
return required_parameters and not specified_parameters
def handle_token_expiration(adapter_sdk: AdapterClient) -> AdapterClient:
"""
Tries to refresh access token. Returns new adapter client. Can return
the save object or new object
"""
at = adapter_sdk.session_token
if at and not JWTToken(at).is_expired():
_LOG.debug('Access token has not expired yet. Using it')
return adapter_sdk
# no access token or expired
rt = ConfigurationProvider().refresh_token
if not rt or JWTToken(rt).is_expired():
_LOG.debug('Refresh token does not exist or expired. Cannot refresh')
return adapter_sdk
resp = adapter_sdk.refresh(rt)
if not resp.ok:
_LOG.warning(f'Could not refresh token: {resp.text}')
return adapter_sdk
data = resp.json()
add_data_to_config(name=CONF_ACCESS_TOKEN, value=data.get('jwt'))
add_data_to_config(name=CONF_REFRESH_TOKEN, value=data.get('refresh_token'))
return init_configuration()