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
57 changes: 21 additions & 36 deletions orchestrator/cli/commands/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from orchestrator.cli.utils.output.prints import (
ADO_NO_ACTIVE_CONTEXT_ERROR,
console_print,
magenta,
)

if typing.TYPE_CHECKING:
Expand All @@ -39,20 +38,12 @@ def manage_contexts(
See https://ibm.github.io/ado/getting-started/ado/#ado-context for
detailed documentation and examples.



Examples:



# View the active context

ado context



# Set local as your active context

ado context local
"""
ado_configuration: AdoConfiguration = ctx.obj
Expand All @@ -70,34 +61,35 @@ def manage_contexts(

def list_contexts(
ctx: typer.Context,
simple: Annotated[
bool,
output_format: Annotated[
AdoGetSupportedOutputFormats,
typer.Option(
"--simple", help="Display only context names.", show_default=False
"--output",
"-o",
help="Output format. Use 'name' to display only context names.",
show_default=False,
),
] = False,
] = AdoGetSupportedOutputFormats.DEFAULT,
) -> None:
"""
List available contexts.

See https://ibm.github.io/ado/getting-started/ado/#ado-context
for detailed documentation and examples.



Examples:



# View available contexts and active context

ado contexts

# List available context names only
ado contexts -o name

# Get contexts as YAML
ado contexts -o yaml

# List available contexts

ado contexts --simple
# Get contexts as JSON
ado contexts -o json
"""
ado_configuration: AdoConfiguration = ctx.obj

Expand All @@ -116,29 +108,22 @@ def list_contexts(
matching_space=None,
minimize_output=True,
no_trunc=False,
output_format=AdoGetSupportedOutputFormats.DEFAULT,
output_format=output_format,
resource_id=None,
resource_type=AdoGetSupportedResourceTypes.CONTEXT,
show_deprecated=False,
show_details=False,
)

# NOTE: there will always be at least one context (local)
get_context(
parameters=parameters,
simplify_output=simple,
)
get_context(parameters=parameters)

if simple:
return

if ado_configuration.active_context is None:
# Warn user if no context is active only when using the DEFAULT format
if (
output_format == AdoGetSupportedOutputFormats.DEFAULT
and ado_configuration.active_context is None
):
console_print(ADO_NO_ACTIVE_CONTEXT_ERROR, stderr=True)
return

console_print(
f"\nThe active context is: {magenta(ado_configuration.active_context)}"
)


def register_context_command(app: typer.Typer) -> None:
Expand All @@ -151,5 +136,5 @@ def register_context_command(app: typer.Typer) -> None:
def register_contexts_command(app: typer.Typer) -> None:
app.command(
name="contexts",
options_metavar="[--simple]",
options_metavar="[--output | -o <format>]",
)(list_contexts)
60 changes: 25 additions & 35 deletions orchestrator/cli/resources/context/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
from orchestrator.cli.models.parameters import AdoGetCommandParameters
from orchestrator.cli.models.types import AdoGetSupportedOutputFormats
from orchestrator.cli.utils.output.prints import (
ADO_INFO_EMPTY_DATAFRAME,
ADO_NO_CONTEXT_AVAILABLE_ERROR,
ERROR,
HINT,
WARN,
console_print,
context_not_in_available_contexts_error_str,
cyan,
Expand All @@ -26,10 +25,9 @@

def get_context(
parameters: AdoGetCommandParameters,
simplify_output: bool = False,
) -> None:
import rich.box

# Never truncate the CONTEXT (name) column
if not parameters.no_trunc:
parameters.no_trunc = ["CONTEXT"]

Expand All @@ -39,11 +37,7 @@ def get_context(
# The only possible way for this should be when the user is
# providing a context with -c and the ado context dir is empty
if len(available_contexts) == 0:
console_print(
f"{WARN}There are no contexts available.\n"
f"{HINT}You can create a context with {cyan('ado create context')}",
stderr=True,
)
console_print(ADO_NO_CONTEXT_AVAILABLE_ERROR, stderr=True)
raise typer.Exit(1)

if parameters.resource_id:
Expand All @@ -65,23 +59,18 @@ def get_context(
parameters.exclude_default = False

if parameters.output_format == AdoGetSupportedOutputFormats.NAME:
# NAME format: output only context identifiers, one per line
# NAME format: output only context names, one per line
for context in sorted(available_contexts):
console_print(context)
return

if parameters.output_format == AdoGetSupportedOutputFormats.DEFAULT:
if simplify_output:
_simple_contexts_formatting(contexts=available_contexts)
return
import rich.box

contexts_df = _prepare_context_dataframe(
contexts=available_contexts,
default_context=parameters.ado_configuration.active_context,
active_context=parameters.ado_configuration.active_context,
)
if contexts_df.empty:
console_print(ADO_INFO_EMPTY_DATAFRAME, stderr=True)
return

console_print(
dataframe_to_rich_table(
Expand All @@ -98,19 +87,25 @@ def get_context(
format_resource_for_ado_get_custom_format,
)

to_print = []
try:
to_print = [
ProjectContext.model_validate(
yaml.safe_load(
parameters.ado_configuration.project_context_path_for_context(
ctx
).read_text()
for ctx in available_contexts:
to_print.append(
ProjectContext.model_validate(
yaml.safe_load(
parameters.ado_configuration.project_context_path_for_context(
ctx
).read_text()
)
)
)
for ctx in available_contexts
]
except pydantic.ValidationError as e:
console_print(f"{ERROR}One of the contexts was not valid:\n{e}", stderr=True)
console_print(
f"{ERROR}Context {cyan(ctx)} was not valid:\n\n{e}\n\n"
f"{HINT}You can manually update the context file at: '{parameters.ado_configuration.project_context_path_for_context(ctx)}'\n"
f"\tAlternatively, delete the context with {cyan(f'ado delete context {ctx}')}",
stderr=True,
)
raise typer.Exit(1) from e

# AP: it's more readable to write this than to
Expand All @@ -125,20 +120,15 @@ def get_context(
)


def _simple_contexts_formatting(contexts: list[str]) -> None:
for context in sorted(contexts):
console_print(context)


def _prepare_context_dataframe(
contexts: list[str], default_context: str | None
contexts: list[str], active_context: str | None
) -> "pd.DataFrame":
import pandas as pd

default_context_column = [
":white_check_mark:" if ctx == default_context else "" for ctx in contexts
active_context_column = [
":white_check_mark:" if ctx == active_context else "" for ctx in contexts
]
output_df = pd.DataFrame({"CONTEXT": contexts, "DEFAULT": default_context_column})
output_df = pd.DataFrame({"CONTEXT": contexts, "ACTIVE": active_context_column})

# Sort contexts by name
output_df = output_df.sort_values(by=["CONTEXT"], axis="rows")
Expand Down
5 changes: 5 additions & 0 deletions orchestrator/cli/utils/output/prints.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
"or activate one with [b cyan]ado context[/b cyan]"
)

ADO_NO_CONTEXT_AVAILABLE_ERROR = (
f"{WARN}There are no contexts available.\n"
f"{HINT}You can create a context with [b cyan]ado create context[/b cyan]"
)

# Spinners
ADO_SPINNER_CONNECTING_TO_DB = "Connecting to the database"
ADO_SPINNER_QUERYING_DB = "Querying the database"
Expand Down
87 changes: 44 additions & 43 deletions tests/ado/context/test_ado_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,33 +254,29 @@ def test_ado_contexts_list_contexts(tmp_path: Path) -> None:
)
assert ado_contexts_default_output_result.exit_code == 0
ado_contexts_default_output_expected_output = (
"┌───────┬────────────────┬─────────┐\n"
"│ INDEX │ CONTEXT │ DEFAULT │\n"
"├───────┼────────────────┼─────────┤\n"
"│ 0 │ first-context │ │\n"
"│ 1 │ local │ ✅ │\n"
"│ 2 │ second-context │ │\n"
"└───────┴────────────────┴─────────┘\n"
"\n"
"The active context is: local\n"
"┌───────┬────────────────┬────────┐\n"
"│ INDEX │ CONTEXT │ ACTIVE │\n"
"├───────┼────────────────┼────────┤\n"
"│ 0 │ first-context │ │\n"
"│ 1 │ local │ ✅ │\n"
"│ 2 │ second-context │ │\n"
"└───────┴────────────────┴────────┘\n"
)
assert (
ado_contexts_default_output_result.output
== ado_contexts_default_output_expected_output
)

# Test with the simple output
ado_contexts_simple_output_result = runner.invoke(
# Test with the name output format
ado_contexts_name_output_result = runner.invoke(
ado,
["--override-ado-app-dir", str(tmp_path), "contexts", "--simple"],
)
assert ado_contexts_simple_output_result.exit_code == 0
ado_contexts_simple_output_expected_output = (
"first-context\nlocal\nsecond-context\n"
["--override-ado-app-dir", str(tmp_path), "contexts", "-o", "name"],
)
assert ado_contexts_name_output_result.exit_code == 0
ado_contexts_name_output_expected_output = "first-context\nlocal\nsecond-context\n"
assert (
ado_contexts_simple_output_result.output
== ado_contexts_simple_output_expected_output
ado_contexts_name_output_result.output
== ado_contexts_name_output_expected_output
)


Expand Down Expand Up @@ -314,26 +310,27 @@ def test_ado_contexts_list_contexts_with_context_and_empty_dir_override(
)
assert ado_contexts_result.output == ado_contexts_expected_output

# Test with the simple output
ado_contexts_simple_output_result = runner.invoke(
# Test with the name output format
ado_contexts_name_output_result = runner.invoke(
ado,
[
"--override-ado-app-dir",
str(tmp_path),
"-c",
context_location,
"contexts",
"--simple",
"-o",
"name",
],
)
assert ado_contexts_simple_output_result.exit_code == 1
ado_contexts_simple_output_expected_output = (
assert ado_contexts_name_output_result.exit_code == 1
ado_contexts_name_output_expected_output = (
"WARN: There are no contexts available.\n"
"HINT: You can create a context with ado create context\n"
)
assert (
ado_contexts_simple_output_result.output
== ado_contexts_simple_output_expected_output
ado_contexts_name_output_result.output
== ado_contexts_name_output_expected_output
)


Expand Down Expand Up @@ -382,31 +379,35 @@ def test_ado_contexts_list_contexts_with_context_and_valid_dir_override(
)
assert ado_contexts_default_output_result.exit_code == 0
ado_contexts_default_output_expected_output = (
"┌───────┬────────────────┬─────────┐\n"
"│ INDEX │ CONTEXT │ DEFAULT │\n"
"├───────┼────────────────┼─────────┤\n"
"│ 0 │ first-context │ │\n"
"│ 1 │ local │ │\n"
"│ 2 │ second-context │ │\n"
"└───────┴────────────────┴─────────┘\n"
"\n"
f"The active context is: {valid_ado_project_context.project}\n"
"┌───────┬────────────────┬────────┐\n"
"│ INDEX │ CONTEXT │ ACTIVE │\n"
"├───────┼────────────────┼────────┤\n"
"│ 0 │ first-context │ │\n"
"│ 1 │ local │ │\n"
"│ 2 │ second-context │ │\n"
"└───────┴────────────────┴────────┘\n"
)
assert (
ado_contexts_default_output_result.output
== ado_contexts_default_output_expected_output
)

# Test with the simple output
ado_contexts_simple_output_result = runner.invoke(
# Test with the name output format
ado_contexts_name_output_result = runner.invoke(
ado,
["--override-ado-app-dir", str(tmp_path), "contexts", "--simple"],
)
assert ado_contexts_simple_output_result.exit_code == 0
ado_contexts_simple_output_expected_output = (
"first-context\nlocal\nsecond-context\n"
[
"--override-ado-app-dir",
str(tmp_path),
"-c",
context_location,
"contexts",
"-o",
"name",
],
)
assert ado_contexts_name_output_result.exit_code == 0
ado_contexts_name_output_expected_output = "first-context\nlocal\nsecond-context\n"
assert (
ado_contexts_simple_output_result.output
== ado_contexts_simple_output_expected_output
ado_contexts_name_output_result.output
== ado_contexts_name_output_expected_output
)
Loading
Loading