From 6a3de8bcd7e793e028f2ed652547bcc63e67a154 Mon Sep 17 00:00:00 2001 From: Loren Yu Date: Wed, 22 Feb 2023 07:56:24 -0800 Subject: [PATCH] Rename application package to src from api (#146) In PR #138 we renamed the route package to api, which makes it api.api. This PR renames the top level package to src to avoid naming issues and to represent the idea that the application contains other things like CLI commands / background jobs. We considered naming it app, but that would conflict with the app folder at the repo root, and it would also create ambiguity with the flask app variable. --- app/Dockerfile | 2 +- app/Makefile | 22 +++++++++---------- app/local.env | 4 ++-- app/pyproject.toml | 14 ++++++------ app/{api => src}/__init__.py | 0 app/{api => src}/__main__.py | 10 ++++----- app/{api => src}/adapters/db/__init__.py | 4 ++-- app/{api => src}/adapters/db/client.py | 2 +- app/{api => src}/adapters/db/config.py | 2 +- app/{api => src}/adapters/db/flask_db.py | 16 +++++++------- app/{api => src}/api/__init__.py | 0 app/{api => src}/api/healthcheck.py | 6 ++--- app/{api => src}/api/response.py | 4 ++-- app/{api => src}/api/schemas/__init__.py | 0 .../api/schemas/request_schema.py | 0 .../api/schemas/response_schema.py | 2 +- app/{api => src}/api/users/__init__.py | 6 ++--- app/{api => src}/api/users/user_blueprint.py | 0 app/{api => src}/api/users/user_commands.py | 10 ++++----- app/{api => src}/api/users/user_routes.py | 18 +++++++-------- app/{api => src}/api/users/user_schemas.py | 4 ++-- app/{api => src}/app.py | 18 +++++++-------- app/{api => src}/app_config.py | 2 +- app/{api => src}/auth/__init__.py | 0 app/{api => src}/auth/api_key_auth.py | 0 app/{api => src}/db/__init__.py | 0 app/{api => src}/db/migrations/__init__.py | 0 app/{api => src}/db/migrations/alembic.ini | 2 +- app/{api => src}/db/migrations/env.py | 14 ++++++------ app/{api => src}/db/migrations/run.py | 0 app/{api => src}/db/migrations/script.py.mako | 0 .../2022_12_16_create_user_and_role_tables.py | 0 .../versions/2023_02_21_cascade_on_delete.py | 0 app/{api => src}/db/models/__init__.py | 0 app/{api => src}/db/models/base.py | 2 +- app/{api => src}/db/models/user_models.py | 2 +- app/{api => src}/logging/__init__.py | 10 ++++----- app/{api => src}/logging/audit.py | 0 app/{api => src}/logging/config.py | 10 ++++----- app/{api => src}/logging/decodelog.py | 2 +- app/{api => src}/logging/flask_logger.py | 8 +++---- app/{api => src}/logging/formatters.py | 2 +- app/{api => src}/logging/pii.py | 4 ++-- app/{api => src}/services/users/__init__.py | 0 .../services/users/create_user.py | 6 ++--- .../services/users/create_user_csv.py | 4 ++-- app/{api => src}/services/users/get_user.py | 4 ++-- app/{api => src}/services/users/patch_user.py | 6 ++--- app/{api => src}/util/__init__.py | 0 app/{api => src}/util/datetime_util.py | 0 app/{api => src}/util/env_config.py | 4 ++-- app/{api => src}/util/file_util.py | 0 app/{api => src}/util/local.py | 0 app/{api => src}/util/string_utils.py | 0 app/tests/conftest.py | 10 ++++----- app/tests/lib/db_testing.py | 4 ++-- app/tests/{api => src}/__init__.py | 0 app/tests/{api => src}/adapters/__init__.py | 0 app/tests/{api => src}/adapters/db/test_db.py | 6 ++--- .../{api => src}/adapters/db/test_flask_db.py | 4 ++-- .../{api => src}/auth/test_api_key_auth.py | 2 +- app/tests/{api => src}/db/__init__.py | 0 app/tests/{api => src}/db/models/__init__.py | 0 app/tests/{api => src}/db/models/factories.py | 8 +++---- .../{api => src}/db/models/test_factories.py | 6 ++--- app/tests/{api => src}/db/test_migrations.py | 4 ++-- app/tests/{api => src}/logging/__init__.py | 0 app/tests/{api => src}/logging/test_audit.py | 4 ++-- .../{api => src}/logging/test_flask_logger.py | 6 ++--- .../{api => src}/logging/test_formatters.py | 2 +- .../{api => src}/logging/test_logging.py | 6 ++--- app/tests/{api => src}/logging/test_pii.py | 2 +- app/tests/{api => src}/route/__init__.py | 0 .../{api => src}/route/test_healthcheck.py | 2 +- .../{api => src}/route/test_user_route.py | 2 +- app/tests/{api => src}/scripts/__init__.py | 0 .../scripts/test_create_user_csv.py | 6 ++--- .../scripts/test_create_user_csv_expected.csv | 0 app/tests/{api => src}/util/__init__.py | 0 .../{api => src}/util/collections/__init__.py | 0 .../{api => src}/util/parametrize_utils.py | 0 .../{api => src}/util/test_datetime_util.py | 2 +- docs/app/README.md | 4 ++-- docs/app/api-details.md | 4 ++-- .../database/database-access-management.md | 6 ++--- docs/app/database/database-migrations.md | 2 +- docs/app/database/database-testing.md | 2 +- docs/app/formatting-and-linting.md | 2 +- .../logging-configuration.md | 10 ++++----- docs/app/writing-tests.md | 6 ++--- 90 files changed, 169 insertions(+), 169 deletions(-) rename app/{api => src}/__init__.py (100%) rename app/{api => src}/__main__.py (88%) rename app/{api => src}/adapters/db/__init__.py (89%) rename app/{api => src}/adapters/db/client.py (99%) rename app/{api => src}/adapters/db/config.py (95%) rename app/{api => src}/adapters/db/flask_db.py (88%) rename app/{api => src}/api/__init__.py (100%) rename app/{api => src}/api/healthcheck.py (89%) rename app/{api => src}/api/response.py (95%) rename app/{api => src}/api/schemas/__init__.py (100%) rename app/{api => src}/api/schemas/request_schema.py (100%) rename app/{api => src}/api/schemas/response_schema.py (95%) rename app/{api => src}/api/users/__init__.py (51%) rename app/{api => src}/api/users/user_blueprint.py (100%) rename app/{api => src}/api/users/user_commands.py (80%) rename app/{api => src}/api/users/user_routes.py (83%) rename app/{api => src}/api/users/user_schemas.py (94%) rename app/{api => src}/app.py (83%) rename app/{api => src}/app_config.py (89%) rename app/{api => src}/auth/__init__.py (100%) rename app/{api => src}/auth/api_key_auth.py (100%) rename app/{api => src}/db/__init__.py (100%) rename app/{api => src}/db/migrations/__init__.py (100%) rename app/{api => src}/db/migrations/alembic.ini (97%) rename app/{api => src}/db/migrations/env.py (89%) rename app/{api => src}/db/migrations/run.py (100%) rename app/{api => src}/db/migrations/script.py.mako (100%) rename app/{api => src}/db/migrations/versions/2022_12_16_create_user_and_role_tables.py (100%) rename app/{api => src}/db/migrations/versions/2023_02_21_cascade_on_delete.py (100%) rename app/{api => src}/db/models/__init__.py (100%) rename app/{api => src}/db/models/base.py (98%) rename app/{api => src}/db/models/user_models.py (96%) rename app/{api => src}/logging/__init__.py (91%) rename app/{api => src}/logging/audit.py (100%) rename app/{api => src}/logging/config.py (93%) rename app/{api => src}/logging/decodelog.py (99%) rename app/{api => src}/logging/flask_logger.py (95%) rename app/{api => src}/logging/formatters.py (97%) rename app/{api => src}/logging/pii.py (97%) rename app/{api => src}/services/users/__init__.py (100%) rename app/{api => src}/services/users/create_user.py (90%) rename app/{api => src}/services/users/create_user_csv.py (96%) rename app/{api => src}/services/users/get_user.py (91%) rename app/{api => src}/services/users/patch_user.py (94%) rename app/{api => src}/util/__init__.py (100%) rename app/{api => src}/util/datetime_util.py (100%) rename app/{api => src}/util/env_config.py (78%) rename app/{api => src}/util/file_util.py (100%) rename app/{api => src}/util/local.py (100%) rename app/{api => src}/util/string_utils.py (100%) rename app/tests/{api => src}/__init__.py (100%) rename app/tests/{api => src}/adapters/__init__.py (100%) rename app/tests/{api => src}/adapters/db/test_db.py (96%) rename app/tests/{api => src}/adapters/db/test_flask_db.py (93%) rename app/tests/{api => src}/auth/test_api_key_auth.py (93%) rename app/tests/{api => src}/db/__init__.py (100%) rename app/tests/{api => src}/db/models/__init__.py (100%) rename app/tests/{api => src}/db/models/factories.py (93%) rename app/tests/{api => src}/db/models/test_factories.py (95%) rename app/tests/{api => src}/db/test_migrations.py (96%) rename app/tests/{api => src}/logging/__init__.py (100%) rename app/tests/{api => src}/logging/test_audit.py (99%) rename app/tests/{api => src}/logging/test_flask_logger.py (96%) rename app/tests/{api => src}/logging/test_formatters.py (97%) rename app/tests/{api => src}/logging/test_logging.py (95%) rename app/tests/{api => src}/logging/test_pii.py (96%) rename app/tests/{api => src}/route/__init__.py (100%) rename app/tests/{api => src}/route/test_healthcheck.py (94%) rename app/tests/{api => src}/route/test_user_route.py (99%) rename app/tests/{api => src}/scripts/__init__.py (100%) rename app/tests/{api => src}/scripts/test_create_user_csv.py (93%) rename app/tests/{api => src}/scripts/test_create_user_csv_expected.csv (100%) rename app/tests/{api => src}/util/__init__.py (100%) rename app/tests/{api => src}/util/collections/__init__.py (100%) rename app/tests/{api => src}/util/parametrize_utils.py (100%) rename app/tests/{api => src}/util/test_datetime_util.py (97%) diff --git a/app/Dockerfile b/app/Dockerfile index 6b944494..9ec44fbb 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -34,4 +34,4 @@ ENV HOST=0.0.0.0 # https://python-poetry.org/docs/basic-usage/#installing-dependencies RUN poetry install # Run the application. -CMD ["poetry", "run", "python", "-m", "api"] +CMD ["poetry", "run", "python", "-m", "src"] diff --git a/app/Makefile b/app/Makefile index 0e8194d5..82dfda55 100644 --- a/app/Makefile +++ b/app/Makefile @@ -10,15 +10,15 @@ APP_NAME := main-app # Note that you can also change the LOG_FORMAT env var to switch # between JSON & human readable format. This is left in place # in the event JSON is output from a process we don't log. -DECODE_LOG := 2>&1 | python3 -u api/logging/util/decodelog.py +DECODE_LOG := 2>&1 | python3 -u src/logging/util/decodelog.py # A few commands need adjustments if they're run in CI, specify those here # TODO - when CI gets hooked up, actually test this. ifdef CI DOCKER_EXEC_ARGS := -T -e CI -e PYTEST_ADDOPTS="--color=yes" - FLAKE8_FORMAT := '::warning file=api/%(path)s,line=%(row)d,col=%(col)d::%(path)s:%(row)d:%(col)d: %(code)s %(text)s' + FLAKE8_FORMAT := '::warning file=src/%(path)s,line=%(row)d,col=%(col)d::%(path)s:%(row)d:%(col)d: %(code)s %(text)s' MYPY_FLAGS := --no-pretty - MYPY_POSTPROC := | perl -pe "s/^(.+):(\d+):(\d+): error: (.*)/::warning file=api\/\1,line=\2,col=\3::\4/" + MYPY_POSTPROC := | perl -pe "s/^(.+):(\d+):(\d+): error: (.*)/::warning file=src\/\1,line=\2,col=\3::\4/" else FLAKE8_FORMAT := default endif @@ -92,7 +92,7 @@ db-recreate: clean-docker-volumes init-db # DB Migrations ######################### -alembic_config := ./api/db/migrations/alembic.ini +alembic_config := ./src/db/migrations/alembic.ini alembic_cmd := $(PY_RUN_CMD) alembic --config $(alembic_config) db-upgrade: ## Apply pending migrations to db @@ -139,7 +139,7 @@ test-watch: $(PY_RUN_CMD) pytest-watch --clear $(args) test-coverage: - $(PY_RUN_CMD) coverage run --branch --source=api -m pytest -m "not audit" $(args) + $(PY_RUN_CMD) coverage run --branch --source=src -m pytest -m "not audit" $(args) $(PY_RUN_CMD) coverage report test-coverage-report: ## Open HTML test coverage report @@ -151,22 +151,22 @@ test-coverage-report: ## Open HTML test coverage report ################################################## format: - $(PY_RUN_CMD) isort --atomic api tests - $(PY_RUN_CMD) black api tests + $(PY_RUN_CMD) isort --atomic src tests + $(PY_RUN_CMD) black src tests format-check: - $(PY_RUN_CMD) isort --atomic --check-only api tests - $(PY_RUN_CMD) black --check api tests + $(PY_RUN_CMD) isort --atomic --check-only src tests + $(PY_RUN_CMD) black --check src tests lint: lint-py lint-py: lint-flake lint-mypy lint-flake: - $(PY_RUN_CMD) flake8 --format=$(FLAKE8_FORMAT) api tests + $(PY_RUN_CMD) flake8 --format=$(FLAKE8_FORMAT) src tests lint-mypy: - $(PY_RUN_CMD) mypy --show-error-codes $(MYPY_FLAGS) api $(MYPY_POSTPROC) + $(PY_RUN_CMD) mypy --show-error-codes $(MYPY_FLAGS) src $(MYPY_POSTPROC) lint-security: # https://bandit.readthedocs.io/en/latest/index.html $(PY_RUN_CMD) bandit -c pyproject.toml -r . --number 3 --skip B101 -ll -x ./.venv diff --git a/app/local.env b/app/local.env index 63f135e9..04c30a3a 100644 --- a/app/local.env +++ b/app/local.env @@ -1,6 +1,6 @@ # Local environment variables # Used by docker-compose and it can be loaded -# by calling load_local_env_vars() from app/api/util/local.py +# by calling load_local_env_vars() from app/src/util/local.py ENVIRONMENT=local PORT=8080 @@ -15,7 +15,7 @@ PYTHONPATH=/app/ # commands that can run in or out # of the Docker container - defaults to outside -FLASK_APP=api.app:create_app +FLASK_APP=src.app:create_app ############################ # Logging diff --git a/app/pyproject.toml b/app/pyproject.toml index eb85e2cd..7e4af099 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -2,7 +2,7 @@ name = "template-application-flask" version = "0.1.0" description = "A template flask API for building ontop of" -packages = [{ include = "api" }] +packages = [{ include = "src" }] authors = ["Nava Engineering "] [tool.poetry.dependencies] @@ -44,9 +44,9 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] -db-migrate-up = "api.db.migrations.run:up" -db-migrate-down = "api.db.migrations.run:down" -db-migrate-down-all = "api.db.migrations.run:downall" +db-migrate-up = "src.db.migrations.run:up" +db-migrate-down = "src.db.migrations.run:down" +db-migrate-down-all = "src.db.migrations.run:downall" [tool.black] line-length = 100 @@ -85,14 +85,14 @@ plugins = ["sqlalchemy.ext.mypy.plugin"] [tool.bandit] # Ignore audit logging test file since test audit logging requires a lot of operations that trigger bandit warnings -exclude_dirs = ["./tests/api/logging/test_audit.py"] +exclude_dirs = ["./tests/src/logging/test_audit.py"] [[tool.mypy.overrides]] # Migrations are generated without "-> None" # for the returns. Rather than require manually # fixing this for every migration generated, # disable the check for that folder. -module = "api.db.migrations.versions.*" +module = "src.db.migrations.versions.*" disallow_untyped_defs = false [tool.pytest.ini_options] @@ -108,5 +108,5 @@ markers = [ "audit: mark a test as a security audit log test, to be run isolated from other tests"] [tool.coverage.run] -omit = ["api/db/migrations/*.py"] +omit = ["src/db/migrations/*.py"] diff --git a/app/api/__init__.py b/app/src/__init__.py similarity index 100% rename from app/api/__init__.py rename to app/src/__init__.py diff --git a/app/api/__main__.py b/app/src/__main__.py similarity index 88% rename from app/api/__main__.py rename to app/src/__main__.py index aab01a51..725a6f2d 100644 --- a/app/api/__main__.py +++ b/app/src/__main__.py @@ -7,10 +7,10 @@ import logging -import api.app -import api.logging -from api.app_config import AppConfig -from api.util.local import load_local_env_vars +import src.app +import src.logging +from src.app_config import AppConfig +from src.util.local import load_local_env_vars logger = logging.getLogger(__package__) @@ -19,7 +19,7 @@ def main() -> None: load_local_env_vars() app_config = AppConfig() - app = api.app.create_app() + app = src.app.create_app() environment = app_config.environment diff --git a/app/api/adapters/db/__init__.py b/app/src/adapters/db/__init__.py similarity index 89% rename from app/api/adapters/db/__init__.py rename to app/src/adapters/db/__init__.py index 9f415607..ddb68e4e 100644 --- a/app/api/adapters/db/__init__.py +++ b/app/src/adapters/db/__init__.py @@ -7,7 +7,7 @@ To use this module with Flask, use the flask_db module. Usage: - import api.adapters.db as db + import src.adapters.db as db db_client = db.init() @@ -23,7 +23,7 @@ """ # Re-export for convenience -from api.adapters.db.client import Connection, DBClient, Session, init +from src.adapters.db.client import Connection, DBClient, Session, init # Do not import flask_db here, because this module is not dependent on any specific framework. # Code can choose to use this module on its own or with the flask_db module depending on needs. diff --git a/app/api/adapters/db/client.py b/app/src/adapters/db/client.py similarity index 99% rename from app/api/adapters/db/client.py rename to app/src/adapters/db/client.py index 36f38eee..3f55e357 100644 --- a/app/api/adapters/db/client.py +++ b/app/src/adapters/db/client.py @@ -17,7 +17,7 @@ import sqlalchemy.pool as pool from sqlalchemy.orm import session -from api.adapters.db.config import DbConfig, get_db_config +from src.adapters.db.config import DbConfig, get_db_config # Re-export the Connection type that is returned by the get_connection() method # to be used for type hints. diff --git a/app/api/adapters/db/config.py b/app/src/adapters/db/config.py similarity index 95% rename from app/api/adapters/db/config.py rename to app/src/adapters/db/config.py index 67475f65..dff1d91f 100644 --- a/app/api/adapters/db/config.py +++ b/app/src/adapters/db/config.py @@ -3,7 +3,7 @@ from pydantic import Field -from api.util.env_config import PydanticBaseEnvConfig +from src.util.env_config import PydanticBaseEnvConfig logger = logging.getLogger(__name__) diff --git a/app/api/adapters/db/flask_db.py b/app/src/adapters/db/flask_db.py similarity index 88% rename from app/api/adapters/db/flask_db.py rename to app/src/adapters/db/flask_db.py index 04f486c3..24960958 100644 --- a/app/api/adapters/db/flask_db.py +++ b/app/src/adapters/db/flask_db.py @@ -5,8 +5,8 @@ of a Flask app and an instance of a DBClient. Example: - import api.adapters.db as db - import api.adapters.db.flask_db as flask_db + import src.adapters.db as db + import src.adapters.db.flask_db as flask_db db_client = db.init() app = APIFlask(__name__) @@ -16,8 +16,8 @@ new database session that lasts for the duration of the request. Example: - import api.adapters.db as db - import api.adapters.db.flask_db as flask_db + import src.adapters.db as db + import src.adapters.db.flask_db as flask_db @app.route("/health") @flask_db.with_db_session @@ -31,7 +31,7 @@ def health(db_session: db.Session): Example: from flask import current_app - import api.adapters.db.flask_db as flask_db + import src.adapters.db.flask_db as flask_db @app.route("/health") def health(): @@ -43,8 +43,8 @@ def health(): from flask import Flask, current_app -import api.adapters.db as db -from api.adapters.db.client import DBClient +import src.adapters.db as db +from src.adapters.db.client import DBClient _FLASK_EXTENSION_KEY = "db" @@ -67,7 +67,7 @@ def get_db(app: Flask) -> DBClient: Example: from flask import current_app - import api.adapters.db.flask_db as flask_db + import src.adapters.db.flask_db as flask_db @app.route("/health") def health(): diff --git a/app/api/api/__init__.py b/app/src/api/__init__.py similarity index 100% rename from app/api/api/__init__.py rename to app/src/api/__init__.py diff --git a/app/api/api/healthcheck.py b/app/src/api/healthcheck.py similarity index 89% rename from app/api/api/healthcheck.py rename to app/src/api/healthcheck.py index 4df442b2..4dc5782b 100644 --- a/app/api/api/healthcheck.py +++ b/app/src/api/healthcheck.py @@ -6,9 +6,9 @@ from sqlalchemy import text from werkzeug.exceptions import ServiceUnavailable -import api.adapters.db.flask_db as flask_db -from api.api import response -from api.api.schemas import request_schema +import src.adapters.db.flask_db as flask_db +from src.api import response +from src.api.schemas import request_schema logger = logging.getLogger(__name__) diff --git a/app/api/api/response.py b/app/src/api/response.py similarity index 95% rename from app/api/api/response.py rename to app/src/api/response.py index 65f6e0bb..70e2ae8f 100644 --- a/app/api/api/response.py +++ b/app/src/api/response.py @@ -1,8 +1,8 @@ import dataclasses from typing import Optional -from api.api.schemas import response_schema -from api.db.models.base import Base +from src.api.schemas import response_schema +from src.db.models.base import Base @dataclasses.dataclass diff --git a/app/api/api/schemas/__init__.py b/app/src/api/schemas/__init__.py similarity index 100% rename from app/api/api/schemas/__init__.py rename to app/src/api/schemas/__init__.py diff --git a/app/api/api/schemas/request_schema.py b/app/src/api/schemas/request_schema.py similarity index 100% rename from app/api/api/schemas/request_schema.py rename to app/src/api/schemas/request_schema.py diff --git a/app/api/api/schemas/response_schema.py b/app/src/api/schemas/response_schema.py similarity index 95% rename from app/api/api/schemas/response_schema.py rename to app/src/api/schemas/response_schema.py index 9ae6f6ac..5d89e85e 100644 --- a/app/api/api/schemas/response_schema.py +++ b/app/src/api/schemas/response_schema.py @@ -1,6 +1,6 @@ from apiflask import fields -from api.api.schemas import request_schema +from src.api.schemas import request_schema class ValidationErrorSchema(request_schema.OrderedSchema): diff --git a/app/api/api/users/__init__.py b/app/src/api/users/__init__.py similarity index 51% rename from app/api/api/users/__init__.py rename to app/src/api/users/__init__.py index 1da0ac9f..8ddfd549 100644 --- a/app/api/api/users/__init__.py +++ b/app/src/api/users/__init__.py @@ -1,10 +1,10 @@ -from api.api.users.user_blueprint import user_blueprint +from src.api.users.user_blueprint import user_blueprint # import user_commands module to register the CLI commands on the user_blueprint -import api.api.users.user_commands # noqa: F401 E402 isort:skip +import src.api.users.user_commands # noqa: F401 E402 isort:skip # import user_commands module to register the API routes on the user_blueprint -import api.api.users.user_routes # noqa: F401 E402 isort:skip +import src.api.users.user_routes # noqa: F401 E402 isort:skip __all__ = ["user_blueprint"] diff --git a/app/api/api/users/user_blueprint.py b/app/src/api/users/user_blueprint.py similarity index 100% rename from app/api/api/users/user_blueprint.py rename to app/src/api/users/user_blueprint.py diff --git a/app/api/api/users/user_commands.py b/app/src/api/users/user_commands.py similarity index 80% rename from app/api/api/users/user_commands.py rename to app/src/api/users/user_commands.py index 515bd4b4..71f23f57 100644 --- a/app/api/api/users/user_commands.py +++ b/app/src/api/users/user_commands.py @@ -4,11 +4,11 @@ import click -import api.adapters.db as db -import api.adapters.db.flask_db as flask_db -import api.services.users as user_service -from api.api.users.user_blueprint import user_blueprint -from api.util.datetime_util import utcnow +import src.adapters.db as db +import src.adapters.db.flask_db as flask_db +import src.services.users as user_service +from src.api.users.user_blueprint import user_blueprint +from src.util.datetime_util import utcnow logger = logging.getLogger(__name__) diff --git a/app/api/api/users/user_routes.py b/app/src/api/users/user_routes.py similarity index 83% rename from app/api/api/users/user_routes.py rename to app/src/api/users/user_routes.py index 5ef77eee..72163d72 100644 --- a/app/api/api/users/user_routes.py +++ b/app/src/api/users/user_routes.py @@ -1,15 +1,15 @@ import logging from typing import Any -import api.adapters.db as db -import api.adapters.db.flask_db as flask_db -import api.api.response as response -import api.api.users.user_schemas as user_schemas -import api.services.users as user_service -import api.services.users as users -from api.api.users.user_blueprint import user_blueprint -from api.auth.api_key_auth import api_key_auth -from api.db.models.user_models import User +import src.adapters.db as db +import src.adapters.db.flask_db as flask_db +import src.api.response as response +import src.api.users.user_schemas as user_schemas +import src.services.users as user_service +import src.services.users as users +from src.api.users.user_blueprint import user_blueprint +from src.auth.api_key_auth import api_key_auth +from src.db.models.user_models import User logger = logging.getLogger(__name__) diff --git a/app/api/api/users/user_schemas.py b/app/src/api/users/user_schemas.py similarity index 94% rename from app/api/api/users/user_schemas.py rename to app/src/api/users/user_schemas.py index cc44c6b1..b0fd2c14 100644 --- a/app/api/api/users/user_schemas.py +++ b/app/src/api/users/user_schemas.py @@ -1,8 +1,8 @@ from apiflask import fields from marshmallow import fields as marshmallow_fields -from api.api.schemas import request_schema -from api.db.models import user_models +from src.api.schemas import request_schema +from src.db.models import user_models class RoleSchema(request_schema.OrderedSchema): diff --git a/app/api/app.py b/app/src/app.py similarity index 83% rename from app/api/app.py rename to app/src/app.py index 4c118c90..f1d72806 100644 --- a/app/api/app.py +++ b/app/src/app.py @@ -6,14 +6,14 @@ from flask import g from werkzeug.exceptions import Unauthorized -import api.adapters.db as db -import api.adapters.db.flask_db as flask_db -import api.logging -import api.logging.flask_logger as flask_logger -from api.api.healthcheck import healthcheck_blueprint -from api.api.schemas import response_schema -from api.api.users import user_blueprint -from api.auth.api_key_auth import User, get_app_security_scheme +import src.adapters.db as db +import src.adapters.db.flask_db as flask_db +import src.logging +import src.logging.flask_logger as flask_logger +from src.api.healthcheck import healthcheck_blueprint +from src.api.schemas import response_schema +from src.api.users import user_blueprint +from src.auth.api_key_auth import User, get_app_security_scheme logger = logging.getLogger(__name__) @@ -21,7 +21,7 @@ def create_app() -> APIFlask: app = APIFlask(__name__) - root_logger = api.logging.init(__package__) + root_logger = src.logging.init(__package__) flask_logger.init_app(root_logger, app) db_client = db.init() diff --git a/app/api/app_config.py b/app/src/app_config.py similarity index 89% rename from app/api/app_config.py rename to app/src/app_config.py index e97be25f..44159955 100644 --- a/app/api/app_config.py +++ b/app/src/app_config.py @@ -1,4 +1,4 @@ -from api.util.env_config import PydanticBaseEnvConfig +from src.util.env_config import PydanticBaseEnvConfig class AppConfig(PydanticBaseEnvConfig): diff --git a/app/api/auth/__init__.py b/app/src/auth/__init__.py similarity index 100% rename from app/api/auth/__init__.py rename to app/src/auth/__init__.py diff --git a/app/api/auth/api_key_auth.py b/app/src/auth/api_key_auth.py similarity index 100% rename from app/api/auth/api_key_auth.py rename to app/src/auth/api_key_auth.py diff --git a/app/api/db/__init__.py b/app/src/db/__init__.py similarity index 100% rename from app/api/db/__init__.py rename to app/src/db/__init__.py diff --git a/app/api/db/migrations/__init__.py b/app/src/db/migrations/__init__.py similarity index 100% rename from app/api/db/migrations/__init__.py rename to app/src/db/migrations/__init__.py diff --git a/app/api/db/migrations/alembic.ini b/app/src/db/migrations/alembic.ini similarity index 97% rename from app/api/db/migrations/alembic.ini rename to app/src/db/migrations/alembic.ini index 8850aced..7f64607f 100644 --- a/app/api/db/migrations/alembic.ini +++ b/app/src/db/migrations/alembic.ini @@ -2,7 +2,7 @@ [alembic] # path to migration scripts -script_location = api/db/migrations +script_location = src/db/migrations file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(slug)s diff --git a/app/api/db/migrations/env.py b/app/src/db/migrations/env.py similarity index 89% rename from app/api/db/migrations/env.py rename to app/src/db/migrations/env.py index 50b105f1..e519705a 100644 --- a/app/api/db/migrations/env.py +++ b/app/src/db/migrations/env.py @@ -6,19 +6,19 @@ from alembic import context # Alembic cli seems to reset the path on load causing issues with local module imports. -# Workaround is to force set the path to the current run directory (top level api folder) +# Workaround is to force set the path to the current run directory (top level src folder) # See database migrations section in `./database/database-migrations.md` for details about running migrations. sys.path.insert(0, ".") # noqa: E402 # Load env vars before anything further -from api.util.local import load_local_env_vars # noqa: E402 isort:skip +from src.util.local import load_local_env_vars # noqa: E402 isort:skip load_local_env_vars() -from api.adapters.db.client import make_connection_uri # noqa: E402 isort:skip -from api.adapters.db.config import get_db_config # noqa: E402 isort:skip -from api.db.models import metadata # noqa: E402 isort:skip -import api.logging # noqa: E402 isort:skip +from src.adapters.db.client import make_connection_uri # noqa: E402 isort:skip +from src.adapters.db.config import get_db_config # noqa: E402 isort:skip +from src.db.models import metadata # noqa: E402 isort:skip +import src.logging # noqa: E402 isort:skip # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -27,7 +27,7 @@ logger = logging.getLogger("migrations") # Initialize logging -api.logging.init("migrations") +src.logging.init("migrations") if not config.get_main_option("sqlalchemy.url"): uri = make_connection_uri(get_db_config()) diff --git a/app/api/db/migrations/run.py b/app/src/db/migrations/run.py similarity index 100% rename from app/api/db/migrations/run.py rename to app/src/db/migrations/run.py diff --git a/app/api/db/migrations/script.py.mako b/app/src/db/migrations/script.py.mako similarity index 100% rename from app/api/db/migrations/script.py.mako rename to app/src/db/migrations/script.py.mako diff --git a/app/api/db/migrations/versions/2022_12_16_create_user_and_role_tables.py b/app/src/db/migrations/versions/2022_12_16_create_user_and_role_tables.py similarity index 100% rename from app/api/db/migrations/versions/2022_12_16_create_user_and_role_tables.py rename to app/src/db/migrations/versions/2022_12_16_create_user_and_role_tables.py diff --git a/app/api/db/migrations/versions/2023_02_21_cascade_on_delete.py b/app/src/db/migrations/versions/2023_02_21_cascade_on_delete.py similarity index 100% rename from app/api/db/migrations/versions/2023_02_21_cascade_on_delete.py rename to app/src/db/migrations/versions/2023_02_21_cascade_on_delete.py diff --git a/app/api/db/models/__init__.py b/app/src/db/models/__init__.py similarity index 100% rename from app/api/db/models/__init__.py rename to app/src/db/models/__init__.py diff --git a/app/api/db/models/base.py b/app/src/db/models/base.py similarity index 98% rename from app/api/db/models/base.py rename to app/src/db/models/base.py index 111f688e..6cb4d9b8 100644 --- a/app/api/db/models/base.py +++ b/app/src/db/models/base.py @@ -10,7 +10,7 @@ from sqlalchemy.orm import declarative_mixin from sqlalchemy.sql.functions import now as sqlnow -from api.util import datetime_util +from src.util import datetime_util # Override the default naming of constraints # to use suffixes instead: diff --git a/app/api/db/models/user_models.py b/app/src/db/models/user_models.py similarity index 96% rename from app/api/db/models/user_models.py rename to app/src/db/models/user_models.py index 6fca730e..b8cf014b 100644 --- a/app/api/db/models/user_models.py +++ b/app/src/db/models/user_models.py @@ -8,7 +8,7 @@ from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, relationship -from api.db.models.base import Base, IdMixin, TimestampMixin +from src.db.models.base import Base, IdMixin, TimestampMixin logger = logging.getLogger(__name__) diff --git a/app/api/logging/__init__.py b/app/src/logging/__init__.py similarity index 91% rename from app/api/logging/__init__.py rename to app/src/logging/__init__.py index f1178bc6..463b4eec 100644 --- a/app/api/logging/__init__.py +++ b/app/src/logging/__init__.py @@ -3,15 +3,15 @@ There are two formatters for the log messages: human-readable and JSON. The formatter that is used is determined by the environment variable LOG_FORMAT. If the environment variable is not set, the JSON formatter -is used by default. See api.logging.formatters for more information. +is used by default. See src.logging.formatters for more information. The logger also adds a PII mask filter to the root logger. See -api.logging.pii for more information. +src.logging.pii for more information. Usage: - import api.logging + import src.logging - api.logging.init("program name") + src.logging.init("program name") Once the module has been initialized, the standard logging module can be used to log messages: @@ -30,7 +30,7 @@ import sys from typing import Any, cast -import api.logging.config as config +import src.logging.config as config logger = logging.getLogger(__name__) _original_argv = tuple(sys.argv) diff --git a/app/api/logging/audit.py b/app/src/logging/audit.py similarity index 100% rename from app/api/logging/audit.py rename to app/src/logging/audit.py diff --git a/app/api/logging/config.py b/app/src/logging/config.py similarity index 93% rename from app/api/logging/config.py rename to app/src/logging/config.py index d58e02af..46ac7324 100644 --- a/app/api/logging/config.py +++ b/app/src/logging/config.py @@ -1,10 +1,10 @@ import logging import sys -import api.logging.audit -import api.logging.formatters as formatters -import api.logging.pii as pii -from api.util.env_config import PydanticBaseEnvConfig +import src.logging.audit +import src.logging.formatters as formatters +import src.logging.pii as pii +from src.util.env_config import PydanticBaseEnvConfig logger = logging.getLogger(__name__) @@ -50,7 +50,7 @@ def configure_logging() -> logging.Logger: logging.root.setLevel(config.level) if config.enable_audit: - api.logging.audit.init() + src.logging.audit.init() # Configure loggers for third party packages logging.getLogger("alembic").setLevel(logging.INFO) diff --git a/app/api/logging/decodelog.py b/app/src/logging/decodelog.py similarity index 99% rename from app/api/logging/decodelog.py rename to app/src/logging/decodelog.py index 3949d49a..c8211f96 100644 --- a/app/api/logging/decodelog.py +++ b/app/src/logging/decodelog.py @@ -94,7 +94,7 @@ def colorize(text: str, color: str) -> str: def color_for_name(name: str) -> str: - if name.startswith("api"): + if name.startswith("src"): return GREEN elif name.startswith("sqlalchemy"): return ORANGE diff --git a/app/api/logging/flask_logger.py b/app/src/logging/flask_logger.py similarity index 95% rename from app/api/logging/flask_logger.py rename to app/src/logging/flask_logger.py index 4cf5c4ed..f837fb0e 100644 --- a/app/api/logging/flask_logger.py +++ b/app/src/logging/flask_logger.py @@ -9,7 +9,7 @@ non-404 request. Usage: - import api.logging.flask_logger as flask_logger + import src.logging.flask_logger as flask_logger logger = logging.getLogger(__name__) app = create_app() @@ -35,7 +35,7 @@ def init_app(app_logger: logging.Logger, app: flask.Flask) -> None: Also configures the app to log every non-404 request using the given logger. Usage: - import api.logging.flask_logger as flask_logger + import src.logging.flask_logger as flask_logger logger = logging.getLogger(__name__) app = create_app() @@ -83,7 +83,7 @@ def _log_start_request() -> None: """Log the start of a request. This function handles the Flask's before_request event. - See https://tedboy.github.io/flask/interface_api.application_object.html#flask.Flask.before_request + See https://tedboy.github.io/flask/interface_src.application_object.html#flask.Flask.before_request Additional info about the request will be in the `extra` field added by `_add_request_context_info_to_log_record` @@ -95,7 +95,7 @@ def _log_end_request(response: flask.Response) -> flask.Response: """Log the end of a request. This function handles the Flask's after_request event. - See https://tedboy.github.io/flask/interface_api.application_object.html#flask.Flask.after_request + See https://tedboy.github.io/flask/interface_src.application_object.html#flask.Flask.after_request Additional info about the request will be in the `extra` field added by `_add_request_context_info_to_log_record` diff --git a/app/api/logging/formatters.py b/app/src/logging/formatters.py similarity index 97% rename from app/api/logging/formatters.py rename to app/src/logging/formatters.py index e93cbe32..abd1d00d 100644 --- a/app/api/logging/formatters.py +++ b/app/src/logging/formatters.py @@ -11,7 +11,7 @@ import logging from datetime import datetime -import api.logging.decodelog as decodelog +import src.logging.decodelog as decodelog class JsonFormatter(logging.Formatter): diff --git a/app/api/logging/pii.py b/app/src/logging/pii.py similarity index 97% rename from app/api/logging/pii.py rename to app/src/logging/pii.py index 67098618..8cce58f9 100644 --- a/app/api/logging/pii.py +++ b/app/src/logging/pii.py @@ -8,7 +8,7 @@ Example: import logging - import api.logging.pii as pii + import src.logging.pii as pii handler = logging.StreamHandler() handler.addFilter(pii.mask_pii) @@ -22,7 +22,7 @@ Example: import logging - import api.logging.pii as pii + import src.logging.pii as pii logger = logging.getLogger(__name__) logger.addFilter(pii.mask_pii) diff --git a/app/api/services/users/__init__.py b/app/src/services/users/__init__.py similarity index 100% rename from app/api/services/users/__init__.py rename to app/src/services/users/__init__.py diff --git a/app/api/services/users/create_user.py b/app/src/services/users/create_user.py similarity index 90% rename from app/api/services/users/create_user.py rename to app/src/services/users/create_user.py index 096f9621..9721594a 100644 --- a/app/api/services/users/create_user.py +++ b/app/src/services/users/create_user.py @@ -1,9 +1,9 @@ from datetime import date from typing import TypedDict -from api.adapters.db import Session -from api.db.models import user_models -from api.db.models.user_models import Role, User +from src.adapters.db import Session +from src.db.models import user_models +from src.db.models.user_models import Role, User class RoleParams(TypedDict): diff --git a/app/api/services/users/create_user_csv.py b/app/src/services/users/create_user_csv.py similarity index 96% rename from app/api/services/users/create_user_csv.py rename to app/src/services/users/create_user_csv.py index 761d288e..959012f6 100644 --- a/app/api/services/users/create_user_csv.py +++ b/app/src/services/users/create_user_csv.py @@ -4,8 +4,8 @@ from smart_open import open as smart_open -import api.adapters.db as db -from api.db.models.user_models import User +import src.adapters.db as db +from src.db.models.user_models import User logger = logging.getLogger(__name__) diff --git a/app/api/services/users/get_user.py b/app/src/services/users/get_user.py similarity index 91% rename from app/api/services/users/get_user.py rename to app/src/services/users/get_user.py index 2947ad69..4af4c61c 100644 --- a/app/api/services/users/get_user.py +++ b/app/src/services/users/get_user.py @@ -1,8 +1,8 @@ import apiflask from sqlalchemy import orm -from api.adapters.db import Session -from api.db.models.user_models import User +from src.adapters.db import Session +from src.db.models.user_models import User # TODO: separate controller and service concerns diff --git a/app/api/services/users/patch_user.py b/app/src/services/users/patch_user.py similarity index 94% rename from app/api/services/users/patch_user.py rename to app/src/services/users/patch_user.py index c71653e4..bb23a22d 100644 --- a/app/api/services/users/patch_user.py +++ b/app/src/services/users/patch_user.py @@ -6,9 +6,9 @@ import apiflask from sqlalchemy import orm -from api.adapters.db import Session -from api.db.models.user_models import Role, User -from api.services.users.create_user import RoleParams +from src.adapters.db import Session +from src.db.models.user_models import Role, User +from src.services.users.create_user import RoleParams class PatchUserParams(TypedDict, total=False): diff --git a/app/api/util/__init__.py b/app/src/util/__init__.py similarity index 100% rename from app/api/util/__init__.py rename to app/src/util/__init__.py diff --git a/app/api/util/datetime_util.py b/app/src/util/datetime_util.py similarity index 100% rename from app/api/util/datetime_util.py rename to app/src/util/datetime_util.py diff --git a/app/api/util/env_config.py b/app/src/util/env_config.py similarity index 78% rename from app/api/util/env_config.py rename to app/src/util/env_config.py index c556c465..cd9c4dc3 100644 --- a/app/api/util/env_config.py +++ b/app/src/util/env_config.py @@ -2,10 +2,10 @@ from pydantic import BaseSettings -import api +import src env_file = os.path.join( - os.path.dirname(os.path.dirname(api.__file__)), + os.path.dirname(os.path.dirname(src.__file__)), "config", "%s.env" % os.getenv("ENVIRONMENT", "local"), ) diff --git a/app/api/util/file_util.py b/app/src/util/file_util.py similarity index 100% rename from app/api/util/file_util.py rename to app/src/util/file_util.py diff --git a/app/api/util/local.py b/app/src/util/local.py similarity index 100% rename from app/api/util/local.py rename to app/src/util/local.py diff --git a/app/api/util/string_utils.py b/app/src/util/string_utils.py similarity index 100% rename from app/api/util/string_utils.py rename to app/src/util/string_utils.py diff --git a/app/tests/conftest.py b/app/tests/conftest.py index 842251b0..f36145e5 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -7,11 +7,11 @@ import moto import pytest -import api.adapters.db as db -import api.app as app_entry -import tests.api.db.models.factories as factories -from api.db import models -from api.util.local import load_local_env_vars +import src.adapters.db as db +import src.app as app_entry +import tests.src.db.models.factories as factories +from src.db import models +from src.util.local import load_local_env_vars from tests.lib import db_testing logger = logging.getLogger(__name__) diff --git a/app/tests/lib/db_testing.py b/app/tests/lib/db_testing.py index 911d113e..05a8fef5 100644 --- a/app/tests/lib/db_testing.py +++ b/app/tests/lib/db_testing.py @@ -3,8 +3,8 @@ import logging import uuid -import api.adapters.db as db -from api.adapters.db.config import get_db_config +import src.adapters.db as db +from src.adapters.db.config import get_db_config logger = logging.getLogger(__name__) diff --git a/app/tests/api/__init__.py b/app/tests/src/__init__.py similarity index 100% rename from app/tests/api/__init__.py rename to app/tests/src/__init__.py diff --git a/app/tests/api/adapters/__init__.py b/app/tests/src/adapters/__init__.py similarity index 100% rename from app/tests/api/adapters/__init__.py rename to app/tests/src/adapters/__init__.py diff --git a/app/tests/api/adapters/db/test_db.py b/app/tests/src/adapters/db/test_db.py similarity index 96% rename from app/tests/api/adapters/db/test_db.py rename to app/tests/src/adapters/db/test_db.py index de68e044..77f2c8f8 100644 --- a/app/tests/api/adapters/db/test_db.py +++ b/app/tests/src/adapters/db/test_db.py @@ -4,9 +4,9 @@ import pytest from sqlalchemy import text -import api.adapters.db as db -from api.adapters.db.client import get_connection_parameters, make_connection_uri, verify_ssl -from api.adapters.db.config import DbConfig, get_db_config +import src.adapters.db as db +from src.adapters.db.client import get_connection_parameters, make_connection_uri, verify_ssl +from src.adapters.db.config import DbConfig, get_db_config class DummyConnectionInfo: diff --git a/app/tests/api/adapters/db/test_flask_db.py b/app/tests/src/adapters/db/test_flask_db.py similarity index 93% rename from app/tests/api/adapters/db/test_flask_db.py rename to app/tests/src/adapters/db/test_flask_db.py index dfca9964..03c5ccd9 100644 --- a/app/tests/api/adapters/db/test_flask_db.py +++ b/app/tests/src/adapters/db/test_flask_db.py @@ -2,8 +2,8 @@ from flask import Flask, current_app from sqlalchemy import text -import api.adapters.db as db -import api.adapters.db.flask_db as flask_db +import src.adapters.db as db +import src.adapters.db.flask_db as flask_db # Define an isolated example Flask app fixture specific to this test module diff --git a/app/tests/api/auth/test_api_key_auth.py b/app/tests/src/auth/test_api_key_auth.py similarity index 93% rename from app/tests/api/auth/test_api_key_auth.py rename to app/tests/src/auth/test_api_key_auth.py index c3434d7b..152cc63f 100644 --- a/app/tests/api/auth/test_api_key_auth.py +++ b/app/tests/src/auth/test_api_key_auth.py @@ -2,7 +2,7 @@ from apiflask import HTTPError from flask import g -from api.auth.api_key_auth import API_AUTH_USER, verify_token +from src.auth.api_key_auth import API_AUTH_USER, verify_token def test_verify_token_success(app, api_auth_token): diff --git a/app/tests/api/db/__init__.py b/app/tests/src/db/__init__.py similarity index 100% rename from app/tests/api/db/__init__.py rename to app/tests/src/db/__init__.py diff --git a/app/tests/api/db/models/__init__.py b/app/tests/src/db/models/__init__.py similarity index 100% rename from app/tests/api/db/models/__init__.py rename to app/tests/src/db/models/__init__.py diff --git a/app/tests/api/db/models/factories.py b/app/tests/src/db/models/factories.py similarity index 93% rename from app/tests/api/db/models/factories.py rename to app/tests/src/db/models/factories.py index 811d634d..41921a10 100644 --- a/app/tests/api/db/models/factories.py +++ b/app/tests/src/db/models/factories.py @@ -15,9 +15,9 @@ import faker from sqlalchemy.orm import scoped_session -import api.adapters.db as db -import api.db.models.user_models as user_models -import api.util.datetime_util as datetime_util +import src.adapters.db as db +import src.db.models.user_models as user_models +import src.util.datetime_util as datetime_util _db_session: Optional[db.Session] = None @@ -67,7 +67,7 @@ class Meta: model = user_models.Role user_id = factory.LazyAttribute(lambda u: u.user.id) - user = factory.SubFactory("tests.api.db.models.factories.UserFactory", roles=[]) + user = factory.SubFactory("tests.src.db.models.factories.UserFactory", roles=[]) type = factory.Iterator([r.value for r in user_models.RoleType]) diff --git a/app/tests/api/db/models/test_factories.py b/app/tests/src/db/models/test_factories.py similarity index 95% rename from app/tests/api/db/models/test_factories.py rename to app/tests/src/db/models/test_factories.py index f1500501..b2c7ef8c 100644 --- a/app/tests/api/db/models/test_factories.py +++ b/app/tests/src/db/models/test_factories.py @@ -2,9 +2,9 @@ import pytest -import api.adapters.db as db -from api.db.models.user_models import User -from tests.api.db.models.factories import RoleFactory, UserFactory +import src.adapters.db as db +from src.db.models.user_models import User +from tests.src.db.models.factories import RoleFactory, UserFactory user_params = { "first_name": "Alvin", diff --git a/app/tests/api/db/test_migrations.py b/app/tests/src/db/test_migrations.py similarity index 96% rename from app/tests/api/db/test_migrations.py rename to app/tests/src/db/test_migrations.py index 60a0babd..160a8766 100644 --- a/app/tests/api/db/test_migrations.py +++ b/app/tests/src/db/test_migrations.py @@ -6,8 +6,8 @@ from alembic.script.revision import MultipleHeads from alembic.util.exc import CommandError -import api.adapters.db as db -from api.db.migrations.run import alembic_cfg +import src.adapters.db as db +from src.db.migrations.run import alembic_cfg from tests.lib import db_testing diff --git a/app/tests/api/logging/__init__.py b/app/tests/src/logging/__init__.py similarity index 100% rename from app/tests/api/logging/__init__.py rename to app/tests/src/logging/__init__.py diff --git a/app/tests/api/logging/test_audit.py b/app/tests/src/logging/test_audit.py similarity index 99% rename from app/tests/api/logging/test_audit.py rename to app/tests/src/logging/test_audit.py index 80d09969..03be39df 100644 --- a/app/tests/api/logging/test_audit.py +++ b/app/tests/src/logging/test_audit.py @@ -1,5 +1,5 @@ # -# Tests for api.logging.audit. +# Tests for src.logging.audit. # import logging @@ -16,7 +16,7 @@ import pytest -import api.logging.audit as audit +import src.logging.audit as audit # Do not run these tests alongside the rest of the test suite since # this tests adds an audit hook that interfere with other tests, diff --git a/app/tests/api/logging/test_flask_logger.py b/app/tests/src/logging/test_flask_logger.py similarity index 96% rename from app/tests/api/logging/test_flask_logger.py rename to app/tests/src/logging/test_flask_logger.py index b2bb14c3..9e5803c3 100644 --- a/app/tests/api/logging/test_flask_logger.py +++ b/app/tests/src/logging/test_flask_logger.py @@ -5,13 +5,13 @@ import pytest from flask import Flask -import api.logging.flask_logger as flask_logger +import src.logging.flask_logger as flask_logger from tests.lib.assertions import assert_dict_contains @pytest.fixture def logger(): - logger = logging.getLogger("api") + logger = logging.getLogger("src") logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) return logger @@ -23,7 +23,7 @@ def app(logger): @app.get("/hello/") def hello(name): - logging.getLogger("api.hello").info(f"hello, {name}!") + logging.getLogger("src.hello").info(f"hello, {name}!") return "ok" flask_logger.init_app(logger, app) diff --git a/app/tests/api/logging/test_formatters.py b/app/tests/src/logging/test_formatters.py similarity index 97% rename from app/tests/api/logging/test_formatters.py rename to app/tests/src/logging/test_formatters.py index ecebe241..28b84d57 100644 --- a/app/tests/api/logging/test_formatters.py +++ b/app/tests/src/logging/test_formatters.py @@ -4,7 +4,7 @@ import pytest -import api.logging.formatters as formatters +import src.logging.formatters as formatters from tests.lib.assertions import assert_dict_contains diff --git a/app/tests/api/logging/test_logging.py b/app/tests/src/logging/test_logging.py similarity index 95% rename from app/tests/api/logging/test_logging.py rename to app/tests/src/logging/test_logging.py index 53d5a078..cb5ceba3 100644 --- a/app/tests/api/logging/test_logging.py +++ b/app/tests/src/logging/test_logging.py @@ -3,8 +3,8 @@ import pytest -import api.logging -import api.logging.formatters as formatters +import src.logging +import src.logging.formatters as formatters def _init_test_logger( @@ -12,7 +12,7 @@ def _init_test_logger( ): caplog.set_level(logging.DEBUG) monkeypatch.setenv("LOG_FORMAT", log_format) - api.logging.init("test_logging") + src.logging.init("test_logging") @pytest.fixture diff --git a/app/tests/api/logging/test_pii.py b/app/tests/src/logging/test_pii.py similarity index 96% rename from app/tests/api/logging/test_pii.py rename to app/tests/src/logging/test_pii.py index 67c4eedf..8b33652d 100644 --- a/app/tests/api/logging/test_pii.py +++ b/app/tests/src/logging/test_pii.py @@ -1,6 +1,6 @@ import pytest -import api.logging.pii as pii +import src.logging.pii as pii @pytest.mark.parametrize( diff --git a/app/tests/api/route/__init__.py b/app/tests/src/route/__init__.py similarity index 100% rename from app/tests/api/route/__init__.py rename to app/tests/src/route/__init__.py diff --git a/app/tests/api/route/test_healthcheck.py b/app/tests/src/route/test_healthcheck.py similarity index 94% rename from app/tests/api/route/test_healthcheck.py rename to app/tests/src/route/test_healthcheck.py index 32524e18..1fe7fd53 100644 --- a/app/tests/api/route/test_healthcheck.py +++ b/app/tests/src/route/test_healthcheck.py @@ -1,4 +1,4 @@ -import api.adapters.db as db +import src.adapters.db as db def test_get_healthcheck_200(client): diff --git a/app/tests/api/route/test_user_route.py b/app/tests/src/route/test_user_route.py similarity index 99% rename from app/tests/api/route/test_user_route.py rename to app/tests/src/route/test_user_route.py index 00a8b80a..f8b38efd 100644 --- a/app/tests/api/route/test_user_route.py +++ b/app/tests/src/route/test_user_route.py @@ -3,7 +3,7 @@ import faker import pytest -from tests.api.util.parametrize_utils import powerset +from tests.src.util.parametrize_utils import powerset fake = faker.Faker() diff --git a/app/tests/api/scripts/__init__.py b/app/tests/src/scripts/__init__.py similarity index 100% rename from app/tests/api/scripts/__init__.py rename to app/tests/src/scripts/__init__.py diff --git a/app/tests/api/scripts/test_create_user_csv.py b/app/tests/src/scripts/test_create_user_csv.py similarity index 93% rename from app/tests/api/scripts/test_create_user_csv.py rename to app/tests/src/scripts/test_create_user_csv.py index 31f0c577..ccd04e1e 100644 --- a/app/tests/api/scripts/test_create_user_csv.py +++ b/app/tests/src/scripts/test_create_user_csv.py @@ -7,9 +7,9 @@ from pytest_lazyfixture import lazy_fixture from smart_open import open as smart_open -import api.adapters.db as db -from api.db.models.user_models import User -from tests.api.db.models.factories import UserFactory +import src.adapters.db as db +from src.db.models.user_models import User +from tests.src.db.models.factories import UserFactory @pytest.fixture diff --git a/app/tests/api/scripts/test_create_user_csv_expected.csv b/app/tests/src/scripts/test_create_user_csv_expected.csv similarity index 100% rename from app/tests/api/scripts/test_create_user_csv_expected.csv rename to app/tests/src/scripts/test_create_user_csv_expected.csv diff --git a/app/tests/api/util/__init__.py b/app/tests/src/util/__init__.py similarity index 100% rename from app/tests/api/util/__init__.py rename to app/tests/src/util/__init__.py diff --git a/app/tests/api/util/collections/__init__.py b/app/tests/src/util/collections/__init__.py similarity index 100% rename from app/tests/api/util/collections/__init__.py rename to app/tests/src/util/collections/__init__.py diff --git a/app/tests/api/util/parametrize_utils.py b/app/tests/src/util/parametrize_utils.py similarity index 100% rename from app/tests/api/util/parametrize_utils.py rename to app/tests/src/util/parametrize_utils.py diff --git a/app/tests/api/util/test_datetime_util.py b/app/tests/src/util/test_datetime_util.py similarity index 97% rename from app/tests/api/util/test_datetime_util.py rename to app/tests/src/util/test_datetime_util.py index 9a86200c..d6bf02d9 100644 --- a/app/tests/api/util/test_datetime_util.py +++ b/app/tests/src/util/test_datetime_util.py @@ -3,7 +3,7 @@ import pytest import pytz -from api.util.datetime_util import adjust_timezone +from src.util.datetime_util import adjust_timezone @pytest.mark.parametrize( diff --git a/docs/app/README.md b/docs/app/README.md index ba0f0742..43bcf83d 100644 --- a/docs/app/README.md +++ b/docs/app/README.md @@ -12,7 +12,7 @@ This is the API layer. It includes a few separate components: ```text root ├── app -│ └── api +│ └── src │ └── auth Authentication code for API │ └── db │ └── models DB model definitions @@ -68,7 +68,7 @@ Running in the native/local approach may require additional packages to be insta Most configuration options are managed by environment variables. -Environment variables for local development are stored in the [local.env](/app/local.env) file. This file is automatically loaded when running. If running within Docker, this file is specified as an `env_file` in the [docker-compose](/docker-compose.yml) file, and loaded [by a script](/app/api/util/local.py) automatically when running most other components outside the container. +Environment variables for local development are stored in the [local.env](/app/local.env) file. This file is automatically loaded when running. If running within Docker, this file is specified as an `env_file` in the [docker-compose](/docker-compose.yml) file, and loaded [by a script](/app/src/util/local.py) automatically when running most other components outside the container. Any environment variables specified directly in the [docker-compose](/docker-compose.yml) file will take precedent over those specified in the [local.env](/app/local.env) file. diff --git a/docs/app/api-details.md b/docs/app/api-details.md index 893199c8..9606e2fa 100644 --- a/docs/app/api-details.md +++ b/docs/app/api-details.md @@ -4,7 +4,7 @@ See [Technical Overview](./technical-overview.md) for details on the technologie Each endpoint is configured in the [openapi.yml](/app/openapi.yml) file which provides basic request validation. Each endpoint specifies an `operationId` that maps to a function defined in the code that will handle the request. -To make handling a request easier, an [ApiContext](/app/api/util/api_context.py) exists which will fetch the DB session, request body, and current user. This can be used like so: +To make handling a request easier, an [ApiContext](/app/src/util/api_context.py) exists which will fetch the DB session, request body, and current user. This can be used like so: ```py def example_post() -> flask.Response: with api_context_manager() as api_context: @@ -39,7 +39,7 @@ All model schemas defined can be found at the bottom of the UI. # Routes ## Health Check -[GET /v1/healthcheck](/app/api/route/healthcheck.py) is an endpoint for checking the health of the service. It verifies that the database is reachable, and that the API service itself is up and running. +[GET /v1/healthcheck](/app/src/route/healthcheck.py) is an endpoint for checking the health of the service. It verifies that the database is reachable, and that the API service itself is up and running. Note this endpoint explicitly does not require authorization so it can be integrated with any automated monitoring you may build. diff --git a/docs/app/database/database-access-management.md b/docs/app/database/database-access-management.md index 267a3987..60faa27f 100644 --- a/docs/app/database/database-access-management.md +++ b/docs/app/database/database-access-management.md @@ -4,7 +4,7 @@ This document describes the best practices and patterns for how the application ## Client Initialization and Configuration -The database client is initialized when the application starts (see [api/\_\_main\_\_.py](../../../app/api/__main__.py). As the database engine that is used to create acquire connections to the database is initialized using the database configuration defined in [db_config.py](../../../app/api/db/db_config.py), which is configured through environment variables. The initialized database client is then stored on the Flask app's [\`extensions\` dictionary](https://flask.palletsprojects.com/en/2.2.x/api/#flask.Flask.extensions) to be used throughout the lifetime of the application. +The database client is initialized when the application starts (see [src/\_\_main\_\_.py](../../../app/src/__main__.py). As the database engine that is used to create acquire connections to the database is initialized using the database configuration defined in [db_config.py](../../../app/src/db/db_config.py), which is configured through environment variables. The initialized database client is then stored on the Flask app's [\`extensions\` dictionary](https://flask.palletsprojects.com/en/2.2.x/src/#flask.Flask.extensions) to be used throughout the lifetime of the application. ## Session Management @@ -16,7 +16,7 @@ For example, **do this** ### right way ### from flask import current_app -import api.adapters.db as db +import src.adapters.db as db def some_service_func(session: db.Session) with db_session.begin(): # start transaction @@ -35,7 +35,7 @@ and **don't do this** ### wrong way ### from flask import current_app -import api.adapters.db as db +import src.adapters.db as db def some_service_func() db_client = db.get_db(current_app) diff --git a/docs/app/database/database-migrations.md b/docs/app/database/database-migrations.md index ec16929b..31f89804 100644 --- a/docs/app/database/database-migrations.md +++ b/docs/app/database/database-migrations.md @@ -31,7 +31,7 @@ $ make db-upgrade
Example: Adding a new column to an existing table: -1. Manually update the database models with the changes ([example_models.py](/app/api/db/models/example_models.py) in this example) +1. Manually update the database models with the changes ([example_models.py](/app/src/db/models/example_models.py) in this example) ```python class ExampleTable(Base): ... diff --git a/docs/app/database/database-testing.md b/docs/app/database/database-testing.md index 7404bc15..4308cbd0 100644 --- a/docs/app/database/database-testing.md +++ b/docs/app/database/database-testing.md @@ -4,7 +4,7 @@ This document describes how the database is managed in the test suite. ## Test Schema -The test suite creates a new PostgreSQL database schema separate from the `public` schema that is used by the application outside of testing. This schema persists throughout the testing session is dropped at the end of the test run. The schema is created by the `db` fixture in [conftest.py](../../../app/tests/conftest.py). The fixture also creates and returns an initialized instance of the [db.DBClient](../../../app/api/db/__init__.py) that can be used to connect to the created schema. +The test suite creates a new PostgreSQL database schema separate from the `public` schema that is used by the application outside of testing. This schema persists throughout the testing session is dropped at the end of the test run. The schema is created by the `db` fixture in [conftest.py](../../../app/tests/conftest.py). The fixture also creates and returns an initialized instance of the [db.DBClient](../../../app/src/db/__init__.py) that can be used to connect to the created schema. Note that [PostgreSQL schemas](https://www.postgresql.org/docs/current/ddl-schemas.html) are entirely different concepts from [Schema objects in OpenAPI specification](https://swagger.io/docs/specification/data-models/). diff --git a/docs/app/formatting-and-linting.md b/docs/app/formatting-and-linting.md index b93b7919..bce9f557 100644 --- a/docs/app/formatting-and-linting.md +++ b/docs/app/formatting-and-linting.md @@ -4,7 +4,7 @@ Run `make format` to run all of the formatters. -When we run migrations via alembic, we autorun the formatters on the generated files. See [alembic.ini](/app/api/db/migrations/alembic.ini) for configuration. +When we run migrations via alembic, we autorun the formatters on the generated files. See [alembic.ini](/app/src/db/migrations/alembic.ini) for configuration. ### Isort [isort](https://pycqa.github.io/isort/) is used to sort our Python imports. Configuration options can be found in [pyproject.toml - tool.isort](/app/pyproject.toml) diff --git a/docs/app/monitoring-and-observability/logging-configuration.md b/docs/app/monitoring-and-observability/logging-configuration.md index 1a62e37b..993b048d 100644 --- a/docs/app/monitoring-and-observability/logging-configuration.md +++ b/docs/app/monitoring-and-observability/logging-configuration.md @@ -2,7 +2,7 @@ ## Overview -This document describes how logging is configured in the application. The logging functionality is defined in the [api.logging](../../../app/api/logging/) package and leverages Python's built-in [logging](https://docs.python.org/3/library/logging.html) framework. +This document describes how logging is configured in the application. The logging functionality is defined in the [src.logging](../../../app/src/logging/) package and leverages Python's built-in [logging](https://docs.python.org/3/library/logging.html) framework. ## Formatting @@ -12,7 +12,7 @@ We have two separate ways of formatting the logs which are controlled by the `LO ```json { - "name": "api.api.healthcheck", + "name": "src.api.healthcheck", "levelname": "INFO", "funcName": "healthcheck_get", "created": "1663261542.0465896", @@ -33,15 +33,15 @@ We have two separate ways of formatting the logs which are controlled by the `LO ## Logging Extra Data in a Request -The [api.logging.flask_logger](../../../app/api/logging/flask_logger.py) module adds logging functionality to Flask applications. It automatically adds useful data from the Flask request object to logs, logs the start and end of requests, and provides a mechanism for developers to dynamically add extra data to all subsequent logs for the current request. +The [src.logging.flask_logger](../../../app/src/logging/flask_logger.py) module adds logging functionality to Flask applications. It automatically adds useful data from the Flask request object to logs, logs the start and end of requests, and provides a mechanism for developers to dynamically add extra data to all subsequent logs for the current request. ## PII Masking -The api.logging.pii](../../../app/api/logging/pii.py) module defines a filter that applies to all logs that automatically masks data fields that look like social security numbers. +The src.logging.pii](../../../app/src/logging/pii.py) module defines a filter that applies to all logs that automatically masks data fields that look like social security numbers. ## Audit Logging -* The [api.logging.audit](../../../app/api/logging/audit.py) module defines a low level audit hook that logs events that may be of interest from a security point of view, such as dynamic code execution and network requests. +* The [src.logging.audit](../../../app/src/logging/audit.py) module defines a low level audit hook that logs events that may be of interest from a security point of view, such as dynamic code execution and network requests. ## Additional Reading diff --git a/docs/app/writing-tests.md b/docs/app/writing-tests.md index e94626f6..69f31bc9 100644 --- a/docs/app/writing-tests.md +++ b/docs/app/writing-tests.md @@ -16,7 +16,7 @@ For this project specifically: - All tests live under `app/tests/` - Under `tests/`, the organization mirrors the source code structure - - The tests for `app/api/route/` are found at `app/test/api/route/` + - The tests for `app/src/route/` are found at `app/test/api/route/` - Create `__init__.py` files for each directory. This helps [avoid name conflicts when pytest is resolving tests](https://docs.pytest.org/en/stable/goodpractices.html#tests-outside-application-code). @@ -66,7 +66,7 @@ They can be imported into tests from the path `tests.helpers`, for example, To facilitate easier setup of test data, most database models have factories via [factory_boy](https://factoryboy.readthedocs.io/) in -`app/api/db/models/factories.py`. +`app/src/db/models/factories.py`. There are a few different ways of [using the factories](https://factoryboy.readthedocs.io/en/stable/#using-factories), termed @@ -92,4 +92,4 @@ FooFactory.build(foo_id=5, name="Bar") ``` would set `foo_id=5` and `name="Bar"` on the generated model, while all other -attributes would use what's configured on the factory class. \ No newline at end of file +attributes would use what's configured on the factory class.