Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utility CLI for DLite Entities Service #17

Draft
wants to merge 44 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d45ff55
Move all non-main service files into a sub-module
CasperWA Apr 21, 2023
6bf9261
Move back uvicorn file as it is referenced in prod
CasperWA Apr 21, 2023
47fda2b
Start of utilities CLI
CasperWA Apr 21, 2023
c204b34
Start of typer CLI module
CasperWA Apr 22, 2023
5a6bb3d
Add typer to `dev` optional dependency
CasperWA Apr 25, 2023
773b488
Flesh out the CLI
CasperWA Apr 25, 2023
c7a9f2b
Add testing
CasperWA Apr 26, 2023
e4f247e
Add tests for entities-service upload
CasperWA Apr 26, 2023
55259b4
Add pytest CI job
CasperWA Apr 26, 2023
8afd6d8
Fix root dir variable for logger.py
CasperWA Apr 26, 2023
d0cbae7
Add cli optional dependency to pytest CI job
CasperWA Apr 26, 2023
491ce06
Try to make pytest CI job run
CasperWA Apr 26, 2023
856ac29
Use .env files for utility CLI
CasperWA Apr 26, 2023
683c5eb
Ensure proper env var prefix
CasperWA Apr 26, 2023
c8e447a
Implement `entities-service delete` CLI command
CasperWA Apr 26, 2023
d1e3c5d
Implement `entities-service get` CLI command
CasperWA Apr 26, 2023
d886034
Implement `entities-service validate` CLI command
CasperWA Apr 26, 2023
c39abb3
Hide not yet implemented CLI commands
CasperWA Apr 26, 2023
331ba33
Minor doc string improvements
CasperWA Apr 26, 2023
332b69e
Create a global settings file for the CLI
CasperWA Apr 28, 2023
b18b2e5
Create _utils subfolder in CLI for CLI utils
CasperWA Apr 28, 2023
161c87e
Implement the `entity-service search` command
CasperWA Apr 28, 2023
6c9d699
Implement `entities-service update` command
CasperWA Apr 28, 2023
8991784
Merge remote-tracking branch 'origin/main' into cwa/close-12-upload-s…
CasperWA Aug 25, 2023
209e46c
Update dependencies
CasperWA Aug 25, 2023
68b561c
Update to pydantic v2
CasperWA Aug 25, 2023
454aa60
Fix 'config show'
CasperWA Aug 25, 2023
5f08fd4
Add --json, --yaml, and --json-one-line options
CasperWA Aug 25, 2023
e77ff9c
Description clarification
CasperWA Aug 25, 2023
dc8c10b
Move around pytest fixtures
CasperWA Aug 25, 2023
fcefbab
Merge remote-tracking branch 'origin/main' into cwa/close-12-upload-s…
CasperWA Oct 27, 2023
c4a90df
Fix imports
CasperWA Oct 27, 2023
24b1066
Merge remote-tracking branch 'origin/main' into cwa/close-12-upload-s…
CasperWA Nov 21, 2023
07bd3ae
Merge remote-tracking branch 'origin/main' into cwa/close-12-upload-s…
CasperWA Nov 22, 2023
aa86225
Use typing.Annotated and make the CLI run
CasperWA Nov 22, 2023
5ad8639
Attempt fixing typer
CasperWA Nov 22, 2023
3a5be34
Revert using Annotated for typer
CasperWA Nov 23, 2023
181d5d8
Merge remote-tracking branch 'origin/main' into cwa/close-12-upload-s…
CasperWA Nov 23, 2023
424df4c
Fix tests after merge
CasperWA Nov 23, 2023
783aad7
Skip testing CLI if using Python 3.12+
CasperWA Nov 23, 2023
6c2b513
Use test client as a context manager.
CasperWA Nov 23, 2023
925ec72
Try passing in the local user+group for docker CI test
CasperWA Nov 23, 2023
01d7d69
Upload code coverage from docker CI job
CasperWA Nov 23, 2023
6710fe4
Properly support Python 3.11
CasperWA Nov 23, 2023
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
15 changes: 14 additions & 1 deletion .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
--name "entity-service" \
--network "host" \
--volume "${PWD}:/app" \
--user "$(id -u):$(id -g)" \
entity-service
sleep 5

Expand All @@ -94,7 +95,7 @@ jobs:
- name: Run tests
run: |
{
pytest -vv --live-backend
pytest -vv --live-backend --cov-report=xml
} || {
echo "Failed! Here's the Docker logs for the service:" &&
docker logs entity-service &&
Expand All @@ -105,6 +106,17 @@ jobs:
exit 1
}

- name: Upload coverage
if: github.repository_owner == 'SINTEF'
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
env_vars: OS,PYTHON
flags: docker
env:
OS: ubuntu-latest
PYTHON: '3.10'

pytest:
runs-on: ubuntu-latest

Expand Down Expand Up @@ -137,6 +149,7 @@ jobs:
with:
fail_ci_if_error: true
env_vars: OS,PYTHON
flags: local
env:
OS: ubuntu-latest
PYTHON: ${{ matrix.python_version }}
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ repos:
rev: v1.7.0
hooks:
- id: mypy
exclude: ^docs/example/.*$$
exclude: ^(docs/example|tests)/.*$
additional_dependencies:
- pydantic>=2
- types-requests
Expand Down
25 changes: 0 additions & 25 deletions dlite_entities_service/backend.py

This file was deleted.

6 changes: 3 additions & 3 deletions dlite_entities_service/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from fastapi import FastAPI, HTTPException, Path, status

from dlite_entities_service import __version__
from dlite_entities_service.backend import ENTITIES_COLLECTION
from dlite_entities_service.config import CONFIG
from dlite_entities_service.logger import setup_logger
from dlite_entities_service.models import VersionedSOFTEntity
from dlite_entities_service.service.backend import ENTITIES_COLLECTION
from dlite_entities_service.service.config import CONFIG
from dlite_entities_service.service.logger import setup_logger

if TYPE_CHECKING: # pragma: no cover
from typing import Any
Expand Down
2 changes: 1 addition & 1 deletion dlite_entities_service/models/soft5.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pydantic import BaseModel, Field, field_validator, model_validator
from pydantic.networks import AnyHttpUrl

from dlite_entities_service.config import CONFIG
from dlite_entities_service.service.config import CONFIG


class SOFT5Dimension(BaseModel):
Expand Down
2 changes: 1 addition & 1 deletion dlite_entities_service/models/soft7.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pydantic import BaseModel, Field, field_validator, model_validator
from pydantic.networks import AnyHttpUrl

from dlite_entities_service.config import CONFIG
from dlite_entities_service.service.config import CONFIG


class SOFT7Property(BaseModel):
Expand Down
Empty file.
43 changes: 43 additions & 0 deletions dlite_entities_service/service/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Backend implementation."""
from __future__ import annotations

from typing import TYPE_CHECKING

from pymongo import MongoClient
from pymongo.errors import WriteConcernError, WriteError

from dlite_entities_service.service.config import CONFIG

if TYPE_CHECKING: # pragma: no cover
from pymongo.collection import Collection


AnyWriteError = (WriteError, WriteConcernError)
"""Any write error exception."""


def get_collection(
uri: str | None = None, username: str | None = None, password: str | None = None
) -> Collection:
"""Get the MongoDB collection for entities."""
client_kwargs = {
"username": username or CONFIG.mongo_user,
"password": password
or (
CONFIG.mongo_password.get_secret_value()
if CONFIG.mongo_password is not None
else None
),
}
for key, value in list(client_kwargs.items()):
if value is None:
client_kwargs.pop(key, None)

mongo_client = MongoClient(
uri or str(CONFIG.mongo_uri),
**client_kwargs,
)
return mongo_client.dlite.entities


ENTITIES_COLLECTION = get_collection()
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def disable_logging():
def _get_service_logger_handlers() -> list[logging.Handler]:
"""Return a list of handlers for the service logger."""
# Create logs directory
root_dir = Path(__file__).resolve().parent.parent.resolve()
root_dir = Path(__file__).resolve().parent.parent.parent.resolve()
logs_dir = root_dir / "logs"
logs_dir.mkdir(exist_ok=True)

Expand Down
5 changes: 5 additions & 0 deletions dlite_entities_service/utils_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Utility CLI

This module contains a CLI with utilities that may be useful when dealing with the
DLite entities service, it's data (backend), and possibly more.
"""
Empty file.
92 changes: 92 additions & 0 deletions dlite_entities_service/utils_cli/_utils/global_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Global settings for the CLI."""
from __future__ import annotations

from pathlib import Path
from typing import Optional

try:
import typer
except ImportError as exc:
raise ImportError(

Check warning on line 10 in dlite_entities_service/utils_cli/_utils/global_settings.py

View check run for this annotation

Codecov / codecov/patch

dlite_entities_service/utils_cli/_utils/global_settings.py#L9-L10

Added lines #L9 - L10 were not covered by tests
"Please install the DLite entities service utility CLI with 'pip install "
f"{Path(__file__).resolve().parent.parent.parent.parent.resolve()}[cli]'"
) from exc

from rich import print # pylint: disable=redefined-builtin

from dlite_entities_service import __version__

STATUS = {"use_service_dotenv": False}

# Type Aliases
OptionalBool = Optional[bool]


def print_version(value: bool) -> None:
"""Print version and exit."""
if value:
print(f"dlite-entities-service version: {__version__}")
raise typer.Exit()

Check warning on line 29 in dlite_entities_service/utils_cli/_utils/global_settings.py

View check run for this annotation

Codecov / codecov/patch

dlite_entities_service/utils_cli/_utils/global_settings.py#L28-L29

Added lines #L28 - L29 were not covered by tests


def global_options(
_: OptionalBool = typer.Option(
None,
"--version",
help="Show version and exit",
is_eager=True,
callback=print_version,
),
use_service_dotenv: bool = typer.Option(
False,
"--use-service-dotenv/--use-cli-dotenv",
help=(
"Use the .env file also used for the DLite Entities Service or one "
"only for the CLI."
),
is_flag=True,
rich_help_panel="Global options",
),
as_json: bool = typer.Option(
False,
"--json",
help=(
"Print output as JSON. (Muting mutually exclusive with --yaml/--yml "
"and --json-one-line.)"
),
is_flag=True,
rich_help_panel="Global options",
),
as_json_one_line: bool = typer.Option(
False,
"--json-one-line",
help=(
"Print output as JSON without new lines. (Muting mutually exclusive "
"with --yaml/--yml and --json.)"
),
is_flag=True,
rich_help_panel="Global options",
),
as_yaml: bool = typer.Option(
False,
"--yaml",
"--yml",
help=(
"Print output as YAML. (Mutually exclusive with --json and "
"--json-one-line.)"
),
is_flag=True,
rich_help_panel="Global options",
),
) -> None:
"""Global options for the CLI."""
STATUS["use_service_dotenv"] = use_service_dotenv

if sum(int(_) for _ in [as_json, as_json_one_line, as_yaml]) > 1:
raise typer.BadParameter(

Check warning on line 86 in dlite_entities_service/utils_cli/_utils/global_settings.py

View check run for this annotation

Codecov / codecov/patch

dlite_entities_service/utils_cli/_utils/global_settings.py#L86

Added line #L86 was not covered by tests
"Cannot use --json, --yaml/--yml, and --json-one-line together in any "
"combination."
)
STATUS["as_json"] = as_json
STATUS["as_json_one_line"] = as_json_one_line
STATUS["as_yaml"] = as_yaml
Loading