Skip to content

Commit

Permalink
Add stub CLI commands to replace mario app
Browse files Browse the repository at this point in the history
Why these changes are being introduced:
This application is a replacement of an existing app written in Go. We
want to ensure that all functionality of the old app (mario) is
rewritten into this  app. Stubbing out all of the expected CLI commands
from mario will provide clear initial documentation in the codebase of
what the desired end functionality should be for this rewrite, and will
allow us to confirm that we've completed replacement of the full mario
app functionality.

How this addresses that need:
* Adds stub CLI commands representing all CLI commands from the old
  mario app, with documentation and expected parameters but no
  functionality.
* Adds initial tests confirming that each command can be executed
  successfully.
* Updates the CLI to group all commands under a single main command, and
  updates existing tests to match.
* Also updates the setup.cfg file to add pydocstyle configuration.

Side effects of this change:
None

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/RDI-124
  • Loading branch information
hakbailey committed Jul 20, 2022
1 parent 075471d commit f505a5d
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 12 deletions.
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ ignore = C0114,C0116,D100,D103,W0012
linters = eradicate,isort,mccabe,pycodestyle,pydocstyle,pyflakes,pylint
max_line_length = 90

[pylama:pydocstyle]
convention = pep257

[tool:pytest]
log_level = DEBUG

79 changes: 73 additions & 6 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,84 @@
from tim.cli import main


def test_cli_no_options(caplog, runner):
result = runner.invoke(main)
def test_main_group_no_options_configures_correctly_and_invokes_result_callback(
caplog, runner
):
result = runner.invoke(main, ["ping"])
assert result.exit_code == 0
assert "Logger 'root' configured with level=INFO" in caplog.text
assert "Running process" in caplog.text
assert "Total time to complete process" in caplog.text


def test_cli_all_options(caplog, runner):
result = runner.invoke(main, ["--verbose"])
def test_main_group_all_options_configures_correctly_and_invokes_result_callback(
caplog, runner
):
result = runner.invoke(main, ["--verbose", "ping"])
assert result.exit_code == 0
assert "Logger 'root' configured with level=DEBUG" in caplog.text
assert "Running process" in caplog.text
assert "Total time to complete process" in caplog.text


def test_aliases(caplog, runner):
result = runner.invoke(main, ["aliases"])
assert result.exit_code == 0
assert "'aliases' command not yet implemented" in caplog.text


def test_indexes(caplog, runner):
result = runner.invoke(main, ["indexes"])
assert result.exit_code == 0
assert "'indexes' command not yet implemented" in caplog.text


def test_ping(caplog, runner):
result = runner.invoke(main, ["ping"])
assert result.exit_code == 0
assert "'ping' command not yet implemented" in caplog.text


def test_ingest_no_options(caplog, runner):
result = runner.invoke(
main,
["ingest", "-s", "aspace", "tests/fixtures/sample-records.json"],
)
assert result.exit_code == 0
assert "'ingest' command not yet implemented" in caplog.text


def test_ingest_all_options(caplog, runner):
result = runner.invoke(
main,
[
"ingest",
"-s",
"dspace",
"-c",
"title",
"--new",
"--auto",
"tests/fixtures/sample-records.json",
],
)
assert result.exit_code == 0
assert "'ingest' command not yet implemented" in caplog.text


def test_promote(caplog, runner):
result = runner.invoke(main, ["promote", "-i", "test-index"])
assert result.exit_code == 0
assert "'promote' command not yet implemented" in caplog.text


def test_reindex(caplog, runner):
result = runner.invoke(
main, ["reindex", "-i", "test-index", "-d", "destination-index"]
)
assert result.exit_code == 0
assert "'reindex' command not yet implemented" in caplog.text


def test_delete(caplog, runner):
result = runner.invoke(main, ["delete", "-i", "test-index"])
assert result.exit_code == 0
assert "'delete' command not yet implemented" in caplog.text
146 changes: 140 additions & 6 deletions tim/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from datetime import timedelta
from time import perf_counter
from typing import Optional

import click

Expand All @@ -9,20 +10,153 @@
logger = logging.getLogger(__name__)


@click.command()
@click.group()
@click.option(
"-v", "--verbose", is_flag=True, help="Pass to log at debug level instead of info"
)
def main(verbose: bool) -> None:
start_time = perf_counter()
@click.pass_context
def main(ctx: click.Context, verbose: bool) -> None:
ctx.ensure_object(dict)
ctx.obj["START_TIME"] = perf_counter()
root_logger = logging.getLogger()
logger.info(configure_logger(root_logger, verbose))
logger.info(configure_sentry())
logger.info("Running process")

# Do things here!

elapsed_time = perf_counter() - start_time
@main.result_callback()
@click.pass_context
def log_process_time(
ctx: click.Context, result: Optional[object], **kwargs: dict # noqa
) -> None:
elapsed_time = perf_counter() - ctx.obj["START_TIME"]
logger.info(
"Total time to complete process: %s", str(timedelta(seconds=elapsed_time))
)


# OpenSearch instance commands


@main.command()
def aliases() -> None:
"""List OpenSearch aliases and their associated indexes.
Find all aliases in the OpenSearch instance. For each alias, list the names of all
indexes associated with that alias in alphabetical order.
"""
logger.info("'aliases' command not yet implemented")


@main.command()
def indexes() -> None:
"""
List all OpenSearch indexes.
Find all indexes in the OpenSearch cluster. List the indexes in alphabetical order
by name. For each index, display: index name, number of documents, health, status,
UUID, size.
"""
logger.info("'indexes' command not yet implemented")


@main.command()
def ping() -> None:
"""
Ping OpenSearch.
Display the following: name, cluster, version, Lucene version.
"""
logger.info("'ping' command not yet implemented")


# Index commands


@main.command()
@click.option(
"-s",
"--source",
required=True,
help="Short name for source of records, e.g. 'dspace'. Will be used to identify or "
"create index according to the naming convention 'source-timestamp",
)
@click.option(
"-c",
"--consumer",
type=click.Choice(["os", "json", "title", "silent"]),
default="os",
show_default=True,
help="Consumer to use. Non-default optioins can be useful for development and "
"troubleshooting.",
)
@click.option(
"--new",
is_flag=True,
help="Create a new index instead of ingesting into the current production index "
"for the source",
)
@click.option(
"--auto",
is_flag=True,
help="Automatically promote index to the production alias on ingest copmletion. "
"Will demote the existing production index for the source if there is one.",
)
@click.argument("filepath", type=click.Path())
def ingest(
source: str, consumer: str, new: bool, auto: bool, filepath: str # noqa
) -> None:
"""
Bulk ingest records into an index.
By default, ingests into the current production index for the provided source."
FILEPATH: path to ingest file, use format "s3://bucketname/objectname" for s3.
"""
logger.info("'ingest' command not yet implemented")


@main.command()
@click.option(
"-i", "--index", required=True, help="Name of the OpenSearch index to promote."
)
def promote(index: str) -> None: # noqa
"""
Promote an index to the production alias.
Demotes the existing production index for the provided source if there is one.
"""
logger.info("'promote' command not yet implemented")


@main.command()
@click.option(
"-i",
"--index",
required=True,
help="Name of the OpenSearch index to copy.",
)
@click.option(
"-d",
"--destination",
required=True,
help="Name of the destination index.",
)
def reindex(index: str, destination: str) -> None: # noqa
"""
Reindex one index to another index.
Copy one index to another. The doc source must be present in the original index.
"""
logger.info("'reindex' command not yet implemented")


@main.command()
@click.option(
"-i",
"--index",
required=True,
help="Name of the OpenSearch index to delete.",
)
def delete(index: str) -> None: # noqa
"""Delete an index."""
logger.info("'delete' command not yet implemented")

0 comments on commit f505a5d

Please sign in to comment.