Skip to content

Commit

Permalink
Merge pull request #55 from canvas-medical/ad/fixes-from-live-demo
Browse files Browse the repository at this point in the history
Fixes from feedback during live demo
  • Loading branch information
aduane authored May 31, 2024
2 parents 6ebf1da + 3c4aa7e commit bcd247b
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 118 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ $ canvas [OPTIONS] COMMAND [ARGS]...

**Options**:

- `--no-ansi`: Disable colorized output
- `--version`
- `--verbose`: Show extra output
- `--install-completion`: Install completion for the current shell.
- `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
- `--help`: Show this message and exit.

**Commands**:
Expand Down
20 changes: 13 additions & 7 deletions canvas_cli/apps/logs/logs.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import json
from typing import Optional
from urllib.parse import urlparse

import typer
import websocket

from canvas_cli.apps.auth.utils import get_default_host, get_or_request_api_token
from canvas_cli.utils.print import print


def _on_message(ws: websocket.WebSocketApp, message: str) -> None:
print.json(message)
message_to_print = message
try:
message_json = json.loads(message)
message_to_print = f"{message_json['timestamp']} | {message_json['message']}"
except ValueError:
pass
print(message_to_print)


def _on_error(ws: websocket.WebSocketApp, error: str) -> None:
print.json(f"Error: {error}", success=False)
print(f"Error: {error}")


def _on_close(ws: websocket.WebSocketApp, close_status_code: str, close_msg: str) -> None:
print.json(f"Connection closed with status code {close_status_code}: {close_msg}")
print(f"Connection closed with status code {close_status_code}: {close_msg}")


def _on_open(ws: websocket.WebSocketApp) -> None:
print.json("Connected to the logging service")
print("Connected to the logging service")


def logs(
Expand All @@ -40,8 +46,8 @@ def logs(
hostname = urlparse(host).hostname
instance = hostname.removesuffix(".canvasmedical.com")

print.json(
f"Connecting to the log stream. Please be patient as there may be a delay before log messages appear."
print(
"Connecting to the log stream. Please be patient as there may be a delay before log messages appear."
)
websocket_uri = f"wss://logs.console.canvasmedical.com/{instance}?token={token}"

Expand Down
51 changes: 25 additions & 26 deletions canvas_cli/apps/plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

from canvas_cli.apps.auth.utils import get_default_host, get_or_request_api_token
from canvas_cli.utils.context import context
from canvas_cli.utils.print import print
from canvas_cli.utils.validators import validate_manifest_file


Expand Down Expand Up @@ -59,11 +58,11 @@ def _get_name_from_metadata(host: str, token: str, package: Path) -> Optional[st
files={"package": open(package, "rb")},
)
except requests.exceptions.RequestException:
print.json(f"Failed to connect to {host}", success=False)
print(f"Failed to connect to {host}")
raise typer.Exit(1)

if metadata_response.status_code != requests.codes.ok:
print.response(metadata_response, success=False)
print(f"Status code {metadata_response.status_code}: {metadata_response.text}")
raise typer.Exit(1)

metadata = metadata_response.json()
Expand All @@ -83,7 +82,7 @@ def init() -> None:
except OutputDirExistsException:
raise typer.BadParameter(f"The supplied directory already exists")

print.json(f"Project created in {project_dir}", project_dir=project_dir)
print(f"Project created in {project_dir}")


def install(
Expand All @@ -109,11 +108,11 @@ def install(
else:
raise typer.BadParameter(f"Plugin '{plugin_name}' needs to be a valid directory")

print.verbose(f"Installing plugin: {built_package_path} into {host}")
print(f"Installing plugin: {built_package_path} into {host}")

url = plugin_url(host)

print.verbose(f"Posting {built_package_path.absolute()} to {url}")
print(f"Posting {built_package_path.absolute()} to {url}")

try:
r = requests.post(
Expand All @@ -123,19 +122,19 @@ def install(
headers={"Authorization": f"Bearer {token}"},
)
except requests.exceptions.RequestException:
print.json(f"Failed to connect to {host}", success=False)
print(f"Failed to connect to {host}")
raise typer.Exit(1)

if r.status_code == requests.codes.created:
print.response(r)
print("Plugin successfully installed!")

# If we got a bad_request, means there's a duplicate plugin and install can't handle that.
# So we need to get the plugin-name from the package and call `update` directly
elif r.status_code == requests.codes.bad_request:
plugin_name = _get_name_from_metadata(host, token, built_package_path)
update(plugin_name, built_package_path, is_enabled=True, host=host)
else:
print.response(r, success=False)
print(f"Status code {r.status_code}: {r.text}")
raise typer.Exit(1)


Expand All @@ -153,7 +152,7 @@ def uninstall(

url = plugin_url(host, name)

print.verbose(f"Uninstalling {name} using {url}")
print(f"Uninstalling {name} using {url}")

token = get_or_request_api_token(host)

Expand All @@ -165,13 +164,13 @@ def uninstall(
},
)
except requests.exceptions.RequestException:
print.json(f"Failed to connect to {host}", success=False)
print(f"Failed to connect to {host}")
raise typer.Exit(1)

if r.status_code == requests.codes.no_content:
print.response(r)
print(r.text)
else:
print.response(r, success=False)
print(f"Status code {r.status_code}: {r.text}")
raise typer.Exit(1)


Expand All @@ -196,13 +195,16 @@ def list(
headers={"Authorization": f"Bearer {token}"},
)
except requests.exceptions.RequestException:
print.json(f"Failed to connect to {host}", success=False)
print(f"Failed to connect to {host}")
raise typer.Exit(1)

if r.status_code == requests.codes.ok:
print.response(r)
for plugin in r.json().get("results", []):
print(
f"{plugin['name']}@{plugin['version']}\t{'enabled' if plugin['is_enabled'] else 'not enabled'}"
)
else:
print.response(r, success=False)
print(f"Status code {r.status_code}: {r.text}")
raise typer.Exit(1)


Expand All @@ -226,16 +228,12 @@ def validate_manifest(
try:
manifest_json = json.loads(manifest.read_text())
except json.JSONDecodeError:
print.json(
"There was a problem loading the manifest file, please ensure it's valid JSON",
success=False,
path=str(plugin_name),
)
print("There was a problem loading the manifest file, please ensure it's valid JSON")
raise typer.Abort()

validate_manifest_file(manifest_json)

print.json(f"Plugin '{plugin_name}' has a valid CANVAS_MANIFEST.json file")
print(f"Plugin '{plugin_name}' has a valid CANVAS_MANIFEST.json file")


def update(
Expand All @@ -262,7 +260,7 @@ def update(

token = get_or_request_api_token(host)

print.verbose(f"Updating plugin {name} from {host} with {is_enabled=}, {package=}")
print(f"Updating plugin {name} from {host} with {is_enabled=}, {package=}")

binary_package = {"package": open(package, "rb")} if package else None

Expand All @@ -276,11 +274,12 @@ def update(
headers={"Authorization": f"Bearer {token}"},
)
except requests.exceptions.RequestException:
print.json(f"Failed to connect to {host}", success=False)
print(f"Failed to connect to {host}")
raise typer.Exit(1)

if r.status_code == requests.codes.ok:
print.response(r)
print("Plugin successfully updated!")

else:
print.response(r, success=False)
print(f"Status code {r.status_code}: {r.text}")
raise typer.Exit(1)
1 change: 0 additions & 1 deletion canvas_cli/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,3 @@ def reset_context_variables() -> None:
which definitely happens with tests run
"""
context._default_host = None
context._verbose = False
14 changes: 2 additions & 12 deletions canvas_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
from canvas_cli.apps import plugin
from canvas_cli.apps.logs import logs as logs_command
from canvas_cli.utils.context import context
from canvas_cli.utils.print import print

APP_NAME = "canvas_cli"

# The main app
app = typer.Typer(no_args_is_help=True)
app = typer.Typer(no_args_is_help=True, rich_markup_mode=None, add_completion=False)

# Commands
app.command(short_help="Create a new plugin")(plugin.init)
Expand All @@ -29,7 +28,7 @@
def version_callback(value: bool) -> None:
"""Method called when the `--version` flag is set. Prints the version and exits the CLI."""
if value:
print.json(f"{APP_NAME} Version: {__version__}", version=__version__)
print(f"{APP_NAME} Version: {__version__}")
raise typer.Exit()


Expand All @@ -54,25 +53,16 @@ def get_or_create_config_file() -> Path:

@app.callback()
def main(
no_ansi: bool = typer.Option(False, "--no-ansi", help="Disable colorized output"),
version: Optional[bool] = typer.Option(
None, "--version", callback=version_callback, is_eager=True
),
verbose: bool = typer.Option(False, "--verbose", help="Show extra output"),
) -> None:
"""Canvas swiss army knife CLI tool."""
# Fetch the config file and load our context from it.
config_file = get_or_create_config_file()

context.load_from_file(config_file)

context.no_ansi = no_ansi

# Set the --verbose flag
if verbose:
context.verbose = verbose
print.verbose("Verbose mode enabled")


if __name__ == "__main__":
app()
26 changes: 1 addition & 25 deletions canvas_cli/utils/context/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ class CLIContext:
# The default host to use for requests
_default_host: str | None = None

# Print extra output
_verbose: bool = False

# If True no colored output is shown
_no_ansi: bool = False

# When the most recently requested api_token will expire
_token_expiration_date: str | None = None

Expand All @@ -60,7 +54,7 @@ def wrapper(self: "CLIContext", *args: Any, **kwargs: Any) -> Any:
fn(self, *args, **kwargs)
value = args[0]

print.verbose(f"Storing {fn.__name__}={value} in the config file")
print(f"Storing {fn.__name__}={value} in the config file")

self._config_file[fn.__name__] = value
with open(self._config_file_path, "w") as f:
Expand Down Expand Up @@ -100,24 +94,6 @@ def default_host(self) -> str | None:
def default_host(self, new_default_host: str | None) -> None:
self._default_host = new_default_host

@property
def verbose(self) -> bool:
"""Enable extra output."""
return self._verbose

@verbose.setter
def verbose(self, new_verbose: bool) -> None:
self._verbose = new_verbose

@property
def no_ansi(self) -> bool:
"""If set removes colorized output."""
return self._no_ansi

@no_ansi.setter
def no_ansi(self, new_no_ansi: bool) -> None:
self._no_ansi = new_no_ansi

@property
def token_expiration_date(self) -> str | None:
"""When the most recently requested api_token will expire."""
Expand Down
16 changes: 1 addition & 15 deletions canvas_cli/utils/print/print.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import Any

from requests import Response
from rich import print as rich_print


class Printer:
Expand Down Expand Up @@ -39,22 +38,9 @@ def json(message: str | list[str] | dict | None, success: bool = True, **kwargs:

Printer._default_print(json.dumps(status))

@staticmethod
def verbose(*args: Any) -> None:
"""Print text only if `verbose` is set in context."""
from canvas_cli.utils.context import context

if context.verbose:
Printer._default_print(*args)

@staticmethod
def _default_print(*args: Any) -> None:
from canvas_cli.utils.context import context

if context.no_ansi:
builtin_print(*args)
else:
rich_print(*args)
builtin_print(*args)


print = Printer()
7 changes: 4 additions & 3 deletions logger/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

from pubsub.pubsub import Publisher


class PubSubLogHandler(logging.Handler):
def __init__(self)-> None:
def __init__(self) -> None:
self.publisher = Publisher()
logging.Handler.__init__(self=self)

Expand All @@ -17,7 +18,7 @@ class PluginLogger:
def __init__(self) -> None:
self.logger = logging.getLogger("plugin_runner_logger")
self.logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(levelname)s %(asctime)s %(message)s')
formatter = logging.Formatter("%(levelname)s %(asctime)s %(message)s")

streaming_handler = logging.StreamHandler()
streaming_handler.setFormatter(formatter)
Expand All @@ -31,7 +32,7 @@ def __init__(self) -> None:
def debug(self, message) -> None:
self.logger.debug(message)

def info(self,message) -> None:
def info(self, message) -> None:
self.logger.info(message)

def warning(self, message) -> None:
Expand Down
Loading

0 comments on commit bcd247b

Please sign in to comment.