fastapi-mongo-base 1.4.2__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_mongo_base-1.4.2/src/fastapi_mongo_base.egg-info → fastapi_mongo_base-1.5.0}/PKG-INFO +12 -1
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/pyproject.toml +16 -3
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/pytest.ini +4 -2
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/__init__.py +2 -2
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/core/app_factory.py +148 -12
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/core/config.py +39 -30
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/core/db.py +37 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/core/exceptions.py +23 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/db/__init__.py +41 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/db/mongo.py +171 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/db/redis.py +155 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/db/sql.py +189 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/__init__.py +97 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/base.py +58 -0
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/exceptions.py → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/handlers.py +27 -110
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/errors/resource_errors.py → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/http.py +27 -22
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/errors/mongodb_errors.py → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/mongodb.py +6 -6
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/redis.py +12 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/resource.py +31 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/sql.py +13 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/i18n/__init__.py +25 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/i18n/languages.py +122 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/i18n/messages.py +85 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/logging/__init__.py +5 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/logging/formatters.py +33 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/middlewares/__init__.py +5 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/middlewares/prometheus.py +5 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/middlewares/timer.py +36 -0
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/monitoring/__init__.py +33 -0
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/middlewares/prometheus.py → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/monitoring/middleware.py +1 -1
- {fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/prometheus → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/monitoring}/mongo.py +13 -1
- {fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/monitoring}/sentry.py +1 -1
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/routes.py +2 -2
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/schemas.py +5 -1
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/sql/__init__.py +24 -0
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/sql.py → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/sql/models.py +3 -6
- fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/sql/session.py +39 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/utils/basic.py +8 -8
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/utils/timezone.py +31 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0/src/fastapi_mongo_base.egg-info}/PKG-INFO +12 -1
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base.egg-info/SOURCES.txt +32 -9
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base.egg-info/requires.txt +13 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/conftest.py +31 -6
- fastapi_mongo_base-1.5.0/tests/test_app.py +43 -0
- fastapi_mongo_base-1.5.0/tests/test_db.py +261 -0
- fastapi_mongo_base-1.4.2/tests/test_resource_errors.py → fastapi_mongo_base-1.5.0/tests/test_http_errors.py +77 -47
- fastapi_mongo_base-1.5.0/tests/test_i18n.py +84 -0
- fastapi_mongo_base-1.5.0/tests/test_middlewares.py +46 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/test_mongodb_errors.py +24 -5
- fastapi_mongo_base-1.5.0/tests/test_monitoring_mongo.py +243 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/test_openapi_errors.py +6 -4
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/test_sentry.py +2 -2
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/test_usso_routes_workspace.py +2 -2
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/db.py +0 -134
- fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/enums.py +0 -226
- fastapi_mongo_base-1.4.2/tests/test_app.py +0 -20
- fastapi_mongo_base-1.4.2/tests/test_db.py +0 -67
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/LICENSE.txt +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/MANIFEST.in +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/README.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/boilerplate.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/changelog.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/contributing.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/endpoints.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/extra.css +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/index.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/installation.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/license.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/quickstart.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/settings.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/tutorial.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/docs/usage.md +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/mkdocs.yml +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/setup.cfg +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/core/__init__.py +0 -0
- /fastapi_mongo_base-1.4.2/src/fastapi_mongo_base/core/error_responses.py → /fastapi_mongo_base-1.5.0/src/fastapi_mongo_base/errors/responses.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/models.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/py.typed +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/tasks.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/utils/__init__.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/utils/bsontools.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/utils/usso_routes.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base.egg-info/dependency_links.txt +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base.egg-info/top_level.txt +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/__init__.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/app/__init__.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/app/logs/app.log +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/app/main.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/app/server.py +0 -0
- {fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/tests/test_apis.py +0 -0
{fastapi_mongo_base-1.4.2/src/fastapi_mongo_base.egg-info → fastapi_mongo_base-1.5.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-mongo-base
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: A simple boilerplate application, including models and schemas and abstract router, for FastAPI with MongoDB
|
|
5
5
|
Author-email: Mahdi Kiani <mahdikiany@gmail.com>
|
|
6
6
|
Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
|
|
@@ -38,14 +38,25 @@ Provides-Extra: usso
|
|
|
38
38
|
Requires-Dist: usso>=0.30.1; extra == "usso"
|
|
39
39
|
Provides-Extra: sql
|
|
40
40
|
Requires-Dist: sqlalchemy>=2.0.43; extra == "sql"
|
|
41
|
+
Requires-Dist: aiosqlite>=0.20.0; extra == "sql"
|
|
42
|
+
Requires-Dist: greenlet>=3.0.0; extra == "sql"
|
|
41
43
|
Provides-Extra: test
|
|
42
44
|
Requires-Dist: pytest; extra == "test"
|
|
43
45
|
Requires-Dist: pytest-cov; extra == "test"
|
|
44
46
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
45
47
|
Requires-Dist: mongomock_motor; extra == "test"
|
|
46
48
|
Requires-Dist: coverage; extra == "test"
|
|
49
|
+
Requires-Dist: sqlalchemy>=2.0.43; extra == "test"
|
|
50
|
+
Requires-Dist: aiosqlite>=0.20.0; extra == "test"
|
|
51
|
+
Requires-Dist: greenlet>=3.0.0; extra == "test"
|
|
52
|
+
Requires-Dist: prometheus-client>=0.25.0; extra == "test"
|
|
53
|
+
Requires-Dist: redis>=8.0.0; extra == "test"
|
|
54
|
+
Provides-Extra: monitoring
|
|
55
|
+
Requires-Dist: prometheus-client>=0.25.0; extra == "monitoring"
|
|
47
56
|
Provides-Extra: prometheus
|
|
48
57
|
Requires-Dist: prometheus-client>=0.25.0; extra == "prometheus"
|
|
58
|
+
Provides-Extra: redis
|
|
59
|
+
Requires-Dist: redis>=5.0.0; extra == "redis"
|
|
49
60
|
Provides-Extra: sentry
|
|
50
61
|
Requires-Dist: sentry-sdk[fastapi]>=2.0.0; extra == "sentry"
|
|
51
62
|
Dynamic: license-file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-mongo-base"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.5.0"
|
|
8
8
|
description = "A simple boilerplate application, including models and schemas and abstract router, for FastAPI with MongoDB"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -40,9 +40,22 @@ dependencies = [
|
|
|
40
40
|
|
|
41
41
|
[project.optional-dependencies]
|
|
42
42
|
usso = ["usso>=0.30.1"]
|
|
43
|
-
sql = ["sqlalchemy>=2.0.43"]
|
|
44
|
-
test = [
|
|
43
|
+
sql = ["sqlalchemy>=2.0.43", "aiosqlite>=0.20.0", "greenlet>=3.0.0"]
|
|
44
|
+
test = [
|
|
45
|
+
"pytest",
|
|
46
|
+
"pytest-cov",
|
|
47
|
+
"pytest-asyncio",
|
|
48
|
+
"mongomock_motor",
|
|
49
|
+
"coverage",
|
|
50
|
+
"sqlalchemy>=2.0.43",
|
|
51
|
+
"aiosqlite>=0.20.0",
|
|
52
|
+
"greenlet>=3.0.0",
|
|
53
|
+
"prometheus-client>=0.25.0",
|
|
54
|
+
"redis>=8.0.0",
|
|
55
|
+
]
|
|
56
|
+
monitoring = ["prometheus-client>=0.25.0"]
|
|
45
57
|
prometheus = ["prometheus-client>=0.25.0"]
|
|
58
|
+
redis = ["redis>=5.0.0"]
|
|
46
59
|
sentry = ["sentry-sdk[fastapi]>=2.0.0"]
|
|
47
60
|
|
|
48
61
|
[project.urls]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
[pytest]
|
|
2
|
-
pythonpath =
|
|
2
|
+
pythonpath =
|
|
3
|
+
src
|
|
4
|
+
.
|
|
3
5
|
testpaths = tests
|
|
4
6
|
|
|
5
7
|
asyncio_mode = auto
|
|
@@ -9,7 +11,7 @@ addopts =
|
|
|
9
11
|
--cov=src
|
|
10
12
|
--cov-report=term-missing
|
|
11
13
|
--cov-report=html
|
|
12
|
-
--cov-fail-under=
|
|
14
|
+
--cov-fail-under=50
|
|
13
15
|
|
|
14
16
|
filterwarnings =
|
|
15
17
|
ignore:.*model_fields.*:DeprecationWarning
|
{fastapi_mongo_base-1.4.2 → fastapi_mongo_base-1.5.0}/src/fastapi_mongo_base/core/app_factory.py
RENAMED
|
@@ -8,26 +8,151 @@ from collections.abc import AsyncGenerator, Callable
|
|
|
8
8
|
from contextlib import asynccontextmanager
|
|
9
9
|
|
|
10
10
|
import fastapi
|
|
11
|
-
from fastapi.responses import RedirectResponse
|
|
11
|
+
from fastapi.responses import JSONResponse, RedirectResponse
|
|
12
12
|
from fastapi.staticfiles import StaticFiles
|
|
13
13
|
|
|
14
|
-
from . import
|
|
15
|
-
from .
|
|
16
|
-
from .sentry import setup_sentry
|
|
14
|
+
from ..errors.handlers import EXCEPTION_HANDLERS
|
|
15
|
+
from ..errors.responses import COMMON_ERROR_RESPONSES, setup_openapi_errors
|
|
16
|
+
from ..monitoring.sentry import setup_sentry
|
|
17
|
+
from . import config, db
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def
|
|
20
|
+
def _is_configured_uri(value: str | None) -> bool:
|
|
21
|
+
return bool(value and str(value).strip())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _use_mongodb(settings: config.Settings | None) -> bool:
|
|
25
|
+
if settings is None:
|
|
26
|
+
return False
|
|
27
|
+
return _is_configured_uri(getattr(settings, "mongo_uri", None))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _use_redis(settings: config.Settings | None) -> bool:
|
|
31
|
+
if settings is None:
|
|
32
|
+
return False
|
|
33
|
+
return _is_configured_uri(getattr(settings, "redis_uri", None))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _use_sql(settings: config.Settings | None) -> bool:
|
|
37
|
+
if settings is None:
|
|
38
|
+
return False
|
|
39
|
+
return _is_configured_uri(getattr(settings, "database_uri", None))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def health(request: fastapi.Request) -> dict[str, object]:
|
|
43
|
+
"""
|
|
44
|
+
Liveness probe endpoint handler.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
request: FastAPI request object.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Dictionary with status and optional version.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
payload: dict[str, object] = {"status": "up"}
|
|
54
|
+
|
|
55
|
+
if request.app.version:
|
|
56
|
+
payload["version"] = request.app.version
|
|
57
|
+
return payload
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def readiness(request: fastapi.Request) -> JSONResponse:
|
|
20
61
|
"""
|
|
21
|
-
|
|
62
|
+
Readiness probe endpoint handler.
|
|
22
63
|
|
|
23
64
|
Args:
|
|
24
65
|
request: FastAPI request object.
|
|
25
66
|
|
|
26
67
|
Returns:
|
|
27
|
-
|
|
68
|
+
JSON response with dependency checks and HTTP 503 when degraded.
|
|
28
69
|
|
|
29
70
|
"""
|
|
30
|
-
|
|
71
|
+
datasources = getattr(request.app.state, "datasources", {})
|
|
72
|
+
mongo_client = getattr(request.app.state, "mongo_client", None)
|
|
73
|
+
redis_client = getattr(request.app.state, "redis_async_client", None)
|
|
74
|
+
sql_session = getattr(request.app.state, "async_session", None)
|
|
75
|
+
|
|
76
|
+
checks: dict[str, str] = {}
|
|
77
|
+
if datasources.get("mongodb"):
|
|
78
|
+
checks["mongodb"] = await db.check_mongodb(mongo_client)
|
|
79
|
+
else:
|
|
80
|
+
checks["mongodb"] = "skipped"
|
|
81
|
+
|
|
82
|
+
if datasources.get("redis"):
|
|
83
|
+
checks["redis"] = await db.check_redis(redis_client)
|
|
84
|
+
else:
|
|
85
|
+
checks["redis"] = "skipped"
|
|
86
|
+
|
|
87
|
+
if datasources.get("sql"):
|
|
88
|
+
checks["sql"] = await db.check_sql(sql_session)
|
|
89
|
+
else:
|
|
90
|
+
checks["sql"] = "skipped"
|
|
91
|
+
|
|
92
|
+
is_ready = "down" not in checks.values()
|
|
93
|
+
payload: dict[str, object] = {
|
|
94
|
+
"status": "up" if is_ready else "degraded",
|
|
95
|
+
"checks": checks,
|
|
96
|
+
}
|
|
97
|
+
if request.app.version:
|
|
98
|
+
payload["version"] = request.app.version
|
|
99
|
+
|
|
100
|
+
return JSONResponse(
|
|
101
|
+
status_code=fastapi.status.HTTP_200_OK
|
|
102
|
+
if is_ready
|
|
103
|
+
else fastapi.status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
104
|
+
content=payload,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def _startup_datasources(
|
|
109
|
+
app: fastapi.FastAPI,
|
|
110
|
+
settings: config.Settings | None,
|
|
111
|
+
) -> None:
|
|
112
|
+
use_mongo = _use_mongodb(settings)
|
|
113
|
+
use_redis = _use_redis(settings)
|
|
114
|
+
use_sql = _use_sql(settings)
|
|
115
|
+
app.state.datasources = {
|
|
116
|
+
"mongodb": use_mongo,
|
|
117
|
+
"redis": use_redis,
|
|
118
|
+
"sql": use_sql,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if use_mongo:
|
|
122
|
+
mongo_db, mongo_client = await db.init_mongo_db(settings)
|
|
123
|
+
else:
|
|
124
|
+
mongo_db, mongo_client = None, None
|
|
125
|
+
app.state.mongo_db = mongo_db
|
|
126
|
+
app.state.mongo_client = mongo_client
|
|
127
|
+
|
|
128
|
+
if use_redis:
|
|
129
|
+
redis_sync, redis_async = db.init_redis(settings)
|
|
130
|
+
else:
|
|
131
|
+
redis_sync, redis_async = None, None
|
|
132
|
+
app.state.redis_sync_client = redis_sync
|
|
133
|
+
app.state.redis_async_client = redis_async
|
|
134
|
+
|
|
135
|
+
if use_sql:
|
|
136
|
+
sql_engine, sql_session = await db.init_sql(settings)
|
|
137
|
+
else:
|
|
138
|
+
sql_engine, sql_session = None, None
|
|
139
|
+
app.state.sql_engine = sql_engine
|
|
140
|
+
app.state.async_session = sql_session
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def _shutdown_datasources(app: fastapi.FastAPI) -> None:
|
|
144
|
+
if getattr(app.state, "mongo_client", None) is not None:
|
|
145
|
+
await db.close_mongo_client(app.state.mongo_client)
|
|
146
|
+
if (
|
|
147
|
+
getattr(app.state, "redis_sync_client", None) is not None
|
|
148
|
+
or getattr(app.state, "redis_async_client", None) is not None
|
|
149
|
+
):
|
|
150
|
+
await db.close_redis(
|
|
151
|
+
getattr(app.state, "redis_sync_client", None),
|
|
152
|
+
getattr(app.state, "redis_async_client", None),
|
|
153
|
+
)
|
|
154
|
+
if getattr(app.state, "sql_engine", None) is not None:
|
|
155
|
+
await db.close_sql(app.state.sql_engine)
|
|
31
156
|
|
|
32
157
|
|
|
33
158
|
@asynccontextmanager
|
|
@@ -53,7 +178,10 @@ async def lifespan(
|
|
|
53
178
|
"""
|
|
54
179
|
if init_functions is None:
|
|
55
180
|
init_functions = []
|
|
56
|
-
|
|
181
|
+
if settings is not None:
|
|
182
|
+
app.state.settings = settings
|
|
183
|
+
|
|
184
|
+
await _startup_datasources(app, settings)
|
|
57
185
|
|
|
58
186
|
if worker:
|
|
59
187
|
app.state.worker = asyncio.create_task(worker())
|
|
@@ -70,6 +198,7 @@ async def lifespan(
|
|
|
70
198
|
finally:
|
|
71
199
|
if worker:
|
|
72
200
|
app.state.worker.cancel()
|
|
201
|
+
await _shutdown_datasources(app)
|
|
73
202
|
logging.info("Shutdown complete")
|
|
74
203
|
|
|
75
204
|
|
|
@@ -85,7 +214,7 @@ def setup_exception_handlers(
|
|
|
85
214
|
**kwargs: Additional keyword arguments.
|
|
86
215
|
|
|
87
216
|
"""
|
|
88
|
-
exception_handlers =
|
|
217
|
+
exception_handlers = EXCEPTION_HANDLERS
|
|
89
218
|
if handlers:
|
|
90
219
|
exception_handlers.update(handlers)
|
|
91
220
|
|
|
@@ -218,6 +347,7 @@ def create_app(
|
|
|
218
347
|
exception_handlers: dict | None = None,
|
|
219
348
|
log_route: bool = False,
|
|
220
349
|
health_route: bool = True,
|
|
350
|
+
readiness_route: bool = True,
|
|
221
351
|
index_route: bool = True,
|
|
222
352
|
**kwargs: object,
|
|
223
353
|
) -> fastapi.FastAPI:
|
|
@@ -238,7 +368,8 @@ def create_app(
|
|
|
238
368
|
license_info: Optional license information dictionary.
|
|
239
369
|
exception_handlers: Optional exception handlers dictionary.
|
|
240
370
|
log_route: Whether to enable log viewing route.
|
|
241
|
-
health_route: Whether to enable health check route.
|
|
371
|
+
health_route: Whether to enable liveness health check route.
|
|
372
|
+
readiness_route: Whether to enable readiness health check route.
|
|
242
373
|
index_route: Whether to enable index redirect route.
|
|
243
374
|
**kwargs: Additional keyword arguments.
|
|
244
375
|
|
|
@@ -274,6 +405,7 @@ def create_app(
|
|
|
274
405
|
exception_handlers=exception_handlers,
|
|
275
406
|
log_route=log_route,
|
|
276
407
|
health_route=health_route,
|
|
408
|
+
readiness_route=readiness_route,
|
|
277
409
|
index_route=index_route,
|
|
278
410
|
**kwargs,
|
|
279
411
|
)
|
|
@@ -290,6 +422,7 @@ def configure_app(
|
|
|
290
422
|
exception_handlers: dict | None = None,
|
|
291
423
|
log_route: bool = False,
|
|
292
424
|
health_route: bool = True,
|
|
425
|
+
readiness_route: bool = True,
|
|
293
426
|
index_route: bool = True,
|
|
294
427
|
**kwargs: object,
|
|
295
428
|
) -> fastapi.FastAPI:
|
|
@@ -303,7 +436,8 @@ def configure_app(
|
|
|
303
436
|
origins: Optional list of allowed CORS origins.
|
|
304
437
|
exception_handlers: Optional exception handlers dictionary.
|
|
305
438
|
log_route: Whether to enable log viewing route.
|
|
306
|
-
health_route: Whether to enable health check route.
|
|
439
|
+
health_route: Whether to enable liveness health check route.
|
|
440
|
+
readiness_route: Whether to enable readiness health check route.
|
|
307
441
|
index_route: Whether to enable index redirect route.
|
|
308
442
|
**kwargs: Additional keyword arguments.
|
|
309
443
|
|
|
@@ -349,6 +483,8 @@ def configure_app(
|
|
|
349
483
|
|
|
350
484
|
if health_route:
|
|
351
485
|
app.get(f"{base_path}/health")(health)
|
|
486
|
+
if readiness_route:
|
|
487
|
+
app.get(f"{base_path}/health/ready")(readiness)
|
|
352
488
|
if log_route:
|
|
353
489
|
app.get(f"{base_path}/logs", include_in_schema=False)(logs)
|
|
354
490
|
if index_route:
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
4
|
import json
|
|
5
|
-
import logging
|
|
6
5
|
import logging.config
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
from typing import Literal
|
|
@@ -11,31 +10,7 @@ from pydantic import Field, field_validator
|
|
|
11
10
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
12
11
|
from singleton import Singleton
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
class JsonFormatter(logging.Formatter):
|
|
16
|
-
"""
|
|
17
|
-
Logging formatter that outputs a single-line structured JSON object.
|
|
18
|
-
|
|
19
|
-
Using json_advanced.dumps ensures special types (datetime, UUID,
|
|
20
|
-
ObjectId, Pydantic models, etc.) are serialised without raising errors.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def format(self, record: logging.LogRecord) -> str:
|
|
24
|
-
"""Serialise *record* to a JSON string."""
|
|
25
|
-
data: dict[str, object] = {
|
|
26
|
-
"timestamp": self.formatTime(record, self.datefmt),
|
|
27
|
-
"level": record.levelname,
|
|
28
|
-
"logger": record.name,
|
|
29
|
-
"filename": record.filename,
|
|
30
|
-
"lineno": record.lineno,
|
|
31
|
-
"funcName": record.funcName,
|
|
32
|
-
"message": record.getMessage(),
|
|
33
|
-
}
|
|
34
|
-
if record.exc_info:
|
|
35
|
-
data["exc_info"] = self.formatException(record.exc_info)
|
|
36
|
-
if record.stack_info:
|
|
37
|
-
data["stack_info"] = self.formatStack(record.stack_info)
|
|
38
|
-
return json.dumps(data, ensure_ascii=False)
|
|
13
|
+
from ..logging.formatters import JsonFormatter
|
|
39
14
|
|
|
40
15
|
|
|
41
16
|
class ProjectSettings(BaseSettings):
|
|
@@ -79,9 +54,7 @@ class ProjectSettings(BaseSettings):
|
|
|
79
54
|
validation_alias="CORS_ORIGINS",
|
|
80
55
|
)
|
|
81
56
|
page_max_limit: int = 100
|
|
82
|
-
mongo_uri: str = Field(
|
|
83
|
-
default="mongodb://localhost:27017/", validation_alias="MONGO_URI"
|
|
84
|
-
)
|
|
57
|
+
mongo_uri: str | None = Field(default=None, validation_alias="MONGO_URI")
|
|
85
58
|
mongo_server_selection_timeout_ms: int = 5000
|
|
86
59
|
mongo_connect_timeout_ms: int = 5000
|
|
87
60
|
sentry_dsn: str | None = Field(default=None, validation_alias="SENTRY_DSN")
|
|
@@ -105,6 +78,34 @@ class ProjectSettings(BaseSettings):
|
|
|
105
78
|
default=False,
|
|
106
79
|
validation_alias="SENTRY_SEND_DEFAULT_PII",
|
|
107
80
|
)
|
|
81
|
+
redis_uri: str | None = Field(default=None, validation_alias="REDIS_URI")
|
|
82
|
+
database_uri: str | None = Field(
|
|
83
|
+
default=None,
|
|
84
|
+
validation_alias="DATABASE_URI",
|
|
85
|
+
)
|
|
86
|
+
database_echo: bool = Field(
|
|
87
|
+
default=False, validation_alias="DATABASE_ECHO"
|
|
88
|
+
)
|
|
89
|
+
database_pool_size: int | None = Field(
|
|
90
|
+
default=None,
|
|
91
|
+
validation_alias="DATABASE_POOL_SIZE",
|
|
92
|
+
)
|
|
93
|
+
database_max_overflow: int | None = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
validation_alias="DATABASE_MAX_OVERFLOW",
|
|
96
|
+
)
|
|
97
|
+
database_pool_timeout: int | None = Field(
|
|
98
|
+
default=None,
|
|
99
|
+
validation_alias="DATABASE_POOL_TIMEOUT",
|
|
100
|
+
)
|
|
101
|
+
database_pool_pre_ping: bool = Field(
|
|
102
|
+
default=True,
|
|
103
|
+
validation_alias="DATABASE_POOL_PRE_PING",
|
|
104
|
+
)
|
|
105
|
+
database_pool_recycle: int | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
validation_alias="DATABASE_POOL_RECYCLE",
|
|
108
|
+
)
|
|
108
109
|
log_format: Literal["json", "text"] = Field(
|
|
109
110
|
default="json",
|
|
110
111
|
validation_alias="LOG_FORMAT",
|
|
@@ -216,7 +217,7 @@ class Settings(metaclass=Singleton):
|
|
|
216
217
|
return ["http://localhost:8000"]
|
|
217
218
|
|
|
218
219
|
page_max_limit: int = project_settings.page_max_limit
|
|
219
|
-
mongo_uri: str = project_settings.mongo_uri
|
|
220
|
+
mongo_uri: str | None = project_settings.mongo_uri
|
|
220
221
|
mongo_server_selection_timeout_ms: int = (
|
|
221
222
|
project_settings.mongo_server_selection_timeout_ms
|
|
222
223
|
)
|
|
@@ -231,6 +232,14 @@ class Settings(metaclass=Singleton):
|
|
|
231
232
|
project_settings.sentry_profiles_sample_rate
|
|
232
233
|
)
|
|
233
234
|
sentry_send_default_pii: bool = project_settings.sentry_send_default_pii
|
|
235
|
+
redis_uri: str | None = project_settings.redis_uri
|
|
236
|
+
database_uri: str | None = project_settings.database_uri
|
|
237
|
+
database_echo: bool = project_settings.database_echo
|
|
238
|
+
database_pool_size: int | None = project_settings.database_pool_size
|
|
239
|
+
database_max_overflow: int | None = project_settings.database_max_overflow
|
|
240
|
+
database_pool_timeout: int | None = project_settings.database_pool_timeout
|
|
241
|
+
database_pool_pre_ping: bool = project_settings.database_pool_pre_ping
|
|
242
|
+
database_pool_recycle: int | None = project_settings.database_pool_recycle
|
|
234
243
|
log_format: str = project_settings.log_format
|
|
235
244
|
|
|
236
245
|
@classmethod
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Backward compatibility shim. Prefer fastapi_mongo_base.db."""
|
|
2
|
+
|
|
3
|
+
from ..db import (
|
|
4
|
+
check_mongodb,
|
|
5
|
+
check_redis,
|
|
6
|
+
check_sql,
|
|
7
|
+
close_mongo_client,
|
|
8
|
+
close_redis,
|
|
9
|
+
close_sql,
|
|
10
|
+
create_sql_tables,
|
|
11
|
+
discover_beanie_document_models,
|
|
12
|
+
get_redis_async_client,
|
|
13
|
+
get_redis_sync_client,
|
|
14
|
+
get_sql_engine,
|
|
15
|
+
get_sql_session_factory,
|
|
16
|
+
init_mongo_db,
|
|
17
|
+
init_redis,
|
|
18
|
+
init_sql,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"check_mongodb",
|
|
23
|
+
"check_redis",
|
|
24
|
+
"check_sql",
|
|
25
|
+
"close_mongo_client",
|
|
26
|
+
"close_redis",
|
|
27
|
+
"close_sql",
|
|
28
|
+
"create_sql_tables",
|
|
29
|
+
"discover_beanie_document_models",
|
|
30
|
+
"get_redis_async_client",
|
|
31
|
+
"get_redis_sync_client",
|
|
32
|
+
"get_sql_engine",
|
|
33
|
+
"get_sql_session_factory",
|
|
34
|
+
"init_mongo_db",
|
|
35
|
+
"init_redis",
|
|
36
|
+
"init_sql",
|
|
37
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Backward-compatible re-exports for exception handlers."""
|
|
2
|
+
|
|
3
|
+
from ..errors.base import BaseHTTPException
|
|
4
|
+
from ..errors.handlers import (
|
|
5
|
+
EXCEPTION_HANDLERS,
|
|
6
|
+
base_http_exception_handler,
|
|
7
|
+
error_messages,
|
|
8
|
+
general_exception_handler,
|
|
9
|
+
mongodb_exception_handler,
|
|
10
|
+
pydantic_exception_handler,
|
|
11
|
+
request_validation_exception_handler,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"EXCEPTION_HANDLERS",
|
|
16
|
+
"BaseHTTPException",
|
|
17
|
+
"base_http_exception_handler",
|
|
18
|
+
"error_messages",
|
|
19
|
+
"general_exception_handler",
|
|
20
|
+
"mongodb_exception_handler",
|
|
21
|
+
"pydantic_exception_handler",
|
|
22
|
+
"request_validation_exception_handler",
|
|
23
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Database connection initialization for MongoDB and Redis."""
|
|
2
|
+
|
|
3
|
+
from .mongo import (
|
|
4
|
+
check_mongodb,
|
|
5
|
+
close_mongo_client,
|
|
6
|
+
discover_beanie_document_models,
|
|
7
|
+
init_mongo_db,
|
|
8
|
+
)
|
|
9
|
+
from .redis import (
|
|
10
|
+
check_redis,
|
|
11
|
+
close_redis,
|
|
12
|
+
get_redis_async_client,
|
|
13
|
+
get_redis_sync_client,
|
|
14
|
+
init_redis,
|
|
15
|
+
)
|
|
16
|
+
from .sql import (
|
|
17
|
+
check_sql,
|
|
18
|
+
close_sql,
|
|
19
|
+
create_sql_tables,
|
|
20
|
+
get_sql_engine,
|
|
21
|
+
get_sql_session_factory,
|
|
22
|
+
init_sql,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"check_mongodb",
|
|
27
|
+
"check_redis",
|
|
28
|
+
"check_sql",
|
|
29
|
+
"close_mongo_client",
|
|
30
|
+
"close_redis",
|
|
31
|
+
"close_sql",
|
|
32
|
+
"create_sql_tables",
|
|
33
|
+
"discover_beanie_document_models",
|
|
34
|
+
"get_redis_async_client",
|
|
35
|
+
"get_redis_sync_client",
|
|
36
|
+
"get_sql_engine",
|
|
37
|
+
"get_sql_session_factory",
|
|
38
|
+
"init_mongo_db",
|
|
39
|
+
"init_redis",
|
|
40
|
+
"init_sql",
|
|
41
|
+
]
|