diff --git a/BUILD.md b/BUILD.md index 0c3fb396..17713073 100644 --- a/BUILD.md +++ b/BUILD.md @@ -23,6 +23,22 @@ When poetry is done installing all dependencies you can start using the tool. poetry run python -m uvicorn tad.main:app ``` +## Database + +We support most SQL database types. You can use the variable `APP_DATABASE_SCHEME` to change the database. The default scheme is sqlite. + +If you change the `models` at tad/models of the application you can generate a new migration file +```shell +alembic revision --autogenerate -m "a message" +``` + +Please make sure you check the auto generated file in tad/migrations/ + +to upgrade to the latest version of the database schema use +```shell +alembic upgrade head +``` + ## Building TAD with Containers Containers allows to package software, make it portable, and isolated. Before you can run a container you first need a container runtime. There are several available, but al lot of users use [docker desktop](https://www.docker.com/products/docker-desktop/). diff --git a/alembic.ini b/alembic.ini index 5f1146fb..bfc48614 100644 --- a/alembic.ini +++ b/alembic.ini @@ -75,10 +75,10 @@ script_location = tad/migrations # black.options = -l 79 REVISION_SCRIPT_FILENAME # lint with attempts to fix using "ruff" - use the exec runner, execute a binary -# hooks = ruff -# ruff.type = exec -# ruff.executable = %(here)s/.venv/bin/ruff -# ruff.options = --fix REVISION_SCRIPT_FILENAME +hooks = ruff +ruff.type = exec +ruff.executable = ruff +ruff.options = check --fix REVISION_SCRIPT_FILENAME # Logging configuration [loggers] diff --git a/tad/core/types.py b/tad/core/types.py new file mode 100644 index 00000000..4848e7f3 --- /dev/null +++ b/tad/core/types.py @@ -0,0 +1,5 @@ +from typing import Literal + +EnvironmentType = Literal["local", "staging", "production"] +LoggingLevelType = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] +DatabaseSchemaType = Literal["sqlite", "postgresql", "mysql", "oracle"] diff --git a/tad/migrations/env.py b/tad/migrations/env.py index a5e1c4ad..51cd0f77 100644 --- a/tad/migrations/env.py +++ b/tad/migrations/env.py @@ -9,17 +9,18 @@ if config.config_file_name is not None: fileConfig(config.config_file_name) -from tad.models import SQLModel # noqa +# todo(berry): automaticly import all models +from tad.models.hero import Hero # noqa -target_metadata = SQLModel.metadata +target_metadata = [Hero.metadata] def get_url(): scheme = os.getenv("SQLALCHEMY_SCHEME", "sqlite") if scheme == "sqlite": - file = os.getenv("SQLITE_FILE", "./database") - return f"{scheme}://{file}" + file = os.getenv("SQLITE_FILE", "/database") + return f"{scheme}:///{file}" user = os.getenv("APP_DATABASE_USER", "postgres") password = os.getenv("APP_DATABASE_PASSWORD", "") diff --git a/tad/migrations/versions/99fb931b1324_.py b/tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py similarity index 50% rename from tad/migrations/versions/99fb931b1324_.py rename to tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py index edd267a8..b3f13cba 100644 --- a/tad/migrations/versions/99fb931b1324_.py +++ b/tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py @@ -1,33 +1,36 @@ -"""empty message +"""Added hero test table -Revision ID: 99fb931b1324 +Revision ID: 17f5b05e9ec3 Revises: -Create Date: 2024-05-02 12:04:56.694709 +Create Date: 2024-05-10 13:57:16.989570 """ from collections.abc import Sequence import sqlalchemy as sa +import sqlmodel.sql.sqltypes from alembic import op # revision identifiers, used by Alembic. -revision: str = "99fb931b1324" +revision: str = "17f5b05e9ec3" down_revision: str | None = None branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### op.create_table( "hero", sa.Column("id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(), nullable=False), - sa.Column("secret_name", sa.String(), nullable=False), - sa.Column("age", sa.Integer(), nullable=True), + sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.PrimaryKeyConstraint("id"), ) + # ### end Alembic commands ### def downgrade() -> None: - pass + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("hero") + # ### end Alembic commands ### diff --git a/tad/models/__init__.py b/tad/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/models.py b/tad/models/hero.py similarity index 100% rename from tad/models.py rename to tad/models/hero.py diff --git a/tad/utils/mask.py b/tad/utils/mask.py index f4613e27..73fc6c8b 100644 --- a/tad/utils/mask.py +++ b/tad/utils/mask.py @@ -1,21 +1,21 @@ from typing import Any -class DataMasker: +class Mask: def __init__(self, mask_value: str = "***MASKED***", mask_keywords: list[str] | None = None): if mask_keywords is None: mask_keywords = [] self.mask_value = mask_value # default keywords to mask - self.keywords: list[str] = ["password", "secret", "database_uri"] + self.keywords: list[str] = ["password", "secret"] self.keywords.extend(mask_keywords or []) - def mask_data( # noqa: C901 + def secrets( # noqa: C901ß self, data: str | list[Any] | dict[Any, Any] | set[Any] ) -> str | list[Any] | dict[Any, Any] | set[Any]: if isinstance(data, dict): - masked_dict: dict[str | int, str | int] = {} + masked_dict: dict[Any, Any] = {} for key, value in data.items(): if isinstance(key, str): for keyword in self.keywords: @@ -29,8 +29,8 @@ def mask_data( # noqa: C901 return masked_dict elif isinstance(data, list): - masked_list: list[str | int] = [] - item: str | int + masked_list: list[Any] = [] + item: Any for item in data: if isinstance(item, str): for keyword in self.keywords: @@ -44,9 +44,9 @@ def mask_data( # noqa: C901 return masked_list elif isinstance(data, set): - masked_set: set[str | int] = set() + masked_set: set[Any] = set() - item: str | int + item: Any for item in data: if isinstance(item, str): for keyword in self.keywords: