Skip to content

Commit

Permalink
✨(cli) make the -b/--backend option a CLI sub-command
Browse files Browse the repository at this point in the history
With the addition of more backends, the usage documentation of
the ralph list/read/write and runserver commands became cluttered
as we list all available backends with all their options.
Thus, to keep the documentation concise, we choose to remove the
-b/--backend option and use a backend sub-command instead.
This allows us to have dedicated usage documentation for each backend.
We also improve backend documentation by parsing method and settings
docstrings to construct option help strings.
  • Loading branch information
SergioSim committed Jan 9, 2024
1 parent dede576 commit e09dcf4
Show file tree
Hide file tree
Showing 19 changed files with 2,315 additions and 886 deletions.
2 changes: 1 addition & 1 deletion .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ RALPH_BACKENDS__DATA__CLICKHOUSE__TEST_TABLE_NAME=test_xapi_events_all

# LRS HTTP backend

RALPH_BACKENDS__DATA__LRS__BASE_URL=http://ralph:secret@0.0.0.0:8100/
RALPH_BACKENDS__DATA__LRS__BASE_URL=http://0.0.0.0:8100/
RALPH_BACKENDS__DATA__LRS__USERNAME=ralph
RALPH_BACKENDS__DATA__LRS__PASSWORD=secret
RALPH_BACKENDS__DATA__LRS__HEADERS__X_EXPERIENCE_API_VERSION=1.0.3
Expand Down
2 changes: 1 addition & 1 deletion docs/commands.md → docs/cli.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Commands
# CLI

::: mkdocs-click
:module: ralph.cli
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ markdown_extensions:
nav:
- Ralph: index.md
- Features:
- CLI: cli.md
- LRS HTTP server: features/api.md
- Backends for data storage: features/backends.md
- Learning statements models: features/models.md
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ cli = [
"bcrypt>=4.0.0",
"click>=8.1.0",
"click-option-group>=0.5.0",
"docstring-parser>=0.15",
"sentry-sdk[fastapi]>=1.9.0",
]
dev = [
Expand Down
4 changes: 2 additions & 2 deletions src/ralph/backends/data/async_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import AsyncIterator, Optional, Union

import websockets
from pydantic import AnyUrl, PositiveInt
from pydantic import AnyUrl, PositiveInt, parse_obj_as
from websockets.http import USER_AGENT

from ralph.backends.data.base import (
Expand Down Expand Up @@ -74,7 +74,7 @@ class Config(BaseSettingsConfig):
env_prefix = "RALPH_BACKENDS__DATA__WS__"

CLIENT_OPTIONS: WSClientOptions = WSClientOptions()
URI: AnyUrl
URI: Optional[AnyUrl] = parse_obj_as(AnyUrl, "ws://localhost:8765")


class AsyncWSDataBackend(BaseAsyncDataBackend[WSDataBackendSettings, str]):
Expand Down
13 changes: 6 additions & 7 deletions src/ralph/backends/data/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,11 @@ async def _queue_records(
await queue.put(None)


DataBackend = TypeVar("DataBackend", BaseDataBackend, BaseAsyncDataBackend)


def get_backend_generic_argument(
backend_class: Type[Union[BaseDataBackend, BaseAsyncDataBackend]],
backend_class: Type[DataBackend],
position: DataBackendArgument,
) -> Optional[Type]:
"""Return the generic argument of `backend_class` at specified `position`."""
Expand Down Expand Up @@ -700,9 +703,7 @@ def get_backend_generic_argument(
return None


def set_backend_settings_class(
backend_class: Type[Union[BaseDataBackend, BaseAsyncDataBackend]]
) -> None:
def set_backend_settings_class(backend_class: Type[DataBackend]) -> None:
"""Set `settings_class` attribute with `Config.env_prefix` for `backend_class`."""
settings_class = get_backend_generic_argument(
backend_class, DataBackendArgument.SETTINGS
Expand All @@ -711,9 +712,7 @@ def set_backend_settings_class(
backend_class.settings_class = settings_class


def set_backend_query_class(
backend_class: Type[Union[BaseDataBackend, BaseAsyncDataBackend]]
) -> None:
def set_backend_query_class(backend_class: Type[DataBackend]) -> None:
"""Set `query_class` attribute for `backend_class`."""
query_class = get_backend_generic_argument(backend_class, DataBackendArgument.QUERY)
if query_class:
Expand Down
15 changes: 8 additions & 7 deletions src/ralph/backends/data/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import logging
from typing import Callable

from ralph.conf import settings

Expand All @@ -16,7 +17,7 @@ class HistoryMixin:
"""

@property
def history(self):
def history(self) -> list:
"""Get backend history."""
logger.debug("Loading history file: %s", str(settings.HISTORY_FILE))

Expand All @@ -25,12 +26,12 @@ def history(self):
with settings.HISTORY_FILE.open(
encoding=settings.LOCALE_ENCODING
) as history_file:
self._history = json.load(history_file)
self._history: list = json.load(history_file)
except FileNotFoundError:
self._history = []
return self._history

def write_history(self, history):
def write_history(self, history: list) -> None:
"""Write given history as a JSON file."""
logger.debug("Writing history file: %s", str(settings.HISTORY_FILE))

Expand All @@ -45,22 +46,22 @@ def write_history(self, history):
# Update history
self._history = history

def clean_history(self, selector):
def clean_history(self, selector: Callable[[dict], bool]) -> None:
"""Clean selected events from the history.
selector: a callable that selects events that need to be removed
"""
self._history = list(filter(lambda event: not selector(event), self.history))
self.write_history(self._history)

def append_to_history(self, event):
def append_to_history(self, event: dict) -> None:
"""Append event to history."""
self.write_history(self.history + [event])

def get_command_history(self, backend_name, command):
def get_command_history(self, backend_name: str, command: str) -> list:
"""Extract entry ids from the history for a given command and backend_name."""

def filter_by_name_and_command(entry):
def filter_by_name_and_command(entry: dict) -> bool:
"""Check whether the history entry matches the backend_name and command."""
return entry.get("backend") == backend_name and (
command in [entry.get("command"), entry.get("action")]
Expand Down
Loading

0 comments on commit e09dcf4

Please sign in to comment.