svc-infra 0.1.185__tar.gz → 0.1.186__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.
- {svc_infra-0.1.185 → svc_infra-0.1.186}/PKG-INFO +1 -1
- {svc_infra-0.1.185 → svc_infra-0.1.186}/pyproject.toml +1 -1
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/user_default.py +22 -11
- {svc_infra-0.1.185 → svc_infra-0.1.186}/README.md +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/README.md +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/add.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/crud_router.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/health.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/http.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/management.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/repository.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/resource.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/service.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/service_hooks.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/session.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/db/uniq.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/catchall.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/error_handlers.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/exceptions.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/routers/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/routers/ping.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/settings.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/app/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/app/env.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/app/logging.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/app/root.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/app/settings.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/integration.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/oauth_router.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/providers.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/settings.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/auth/users.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/cmds/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/cmds/alembic_cmds.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/cmds/help.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/cmds/scaffold_cmds.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/foundation/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/foundation/runner.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/cli/foundation/typer_bootstrap.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/README.md +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/base.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/constants.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/core.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/scaffold.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/models_schemas/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/models_schemas/auth/models.py.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/models_schemas/auth/schemas.py.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/models_schemas/entity/models.py.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/models_schemas/entity/schemas.py.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/setup/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/setup/alembic.ini.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/setup/env_async.py.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/setup/env_sync.py.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/setup/script.py.mako.tmpl +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/uniq.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/utils.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/mcp/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/mcp/svc_infra_mcp.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/README.md +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/metrics/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/metrics/asgi.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/metrics/base.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/metrics/http.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/metrics/sqlalchemy.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/settings.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/templates/grafana_dashboard.json +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/templates/otel-collector.yaml +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/templates/prometheus_rules.yml +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/tracing/__init__.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/tracing/setup.py +0 -0
- {svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/py.typed +0 -0
@@ -2,6 +2,7 @@ from typing import Any, Dict
|
|
2
2
|
from fastapi import HTTPException
|
3
3
|
from fastapi_users.password import PasswordHelper
|
4
4
|
from sqlalchemy import func
|
5
|
+
from sqlalchemy.exc import IntegrityError
|
5
6
|
|
6
7
|
from svc_infra.api.fastapi.db.service_hooks import ServiceWithHooks
|
7
8
|
from svc_infra.api.fastapi.db.repository import Repository
|
@@ -9,7 +10,7 @@ from svc_infra.api.fastapi.db.repository import Repository
|
|
9
10
|
_pwd = PasswordHelper()
|
10
11
|
|
11
12
|
def make_default_user_service(repo: Repository):
|
12
|
-
Model = repo.model #
|
13
|
+
Model = repo.model # capture the mapped class
|
13
14
|
|
14
15
|
def _user_pre_create(data: Dict[str, Any]) -> Dict[str, Any]:
|
15
16
|
data = dict(data)
|
@@ -20,11 +21,10 @@ def make_default_user_service(repo: Repository):
|
|
20
21
|
data["extra"] = data.pop("metadata")
|
21
22
|
data.setdefault("roles", [])
|
22
23
|
|
23
|
-
#
|
24
|
+
# attach existence checker (case-insensitive email, tenant scoped)
|
24
25
|
email = data.get("email")
|
25
26
|
tenant_id = data.get("tenant_id")
|
26
27
|
if email is not None:
|
27
|
-
# case-insensitive email; scope by tenant (NULL => global)
|
28
28
|
where = [func.lower(Model.email) == func.lower(email)]
|
29
29
|
if hasattr(Model, "tenant_id"):
|
30
30
|
if tenant_id is None:
|
@@ -35,9 +35,7 @@ def make_default_user_service(repo: Repository):
|
|
35
35
|
async def _exists(session):
|
36
36
|
return await repo.exists(session, where=where)
|
37
37
|
|
38
|
-
# stash the callback so Service.create can await it (has session)
|
39
38
|
data["_precreate_exists_check"] = _exists
|
40
|
-
|
41
39
|
return data
|
42
40
|
|
43
41
|
def _user_pre_update(data: Dict[str, Any]) -> Dict[str, Any]:
|
@@ -47,7 +45,7 @@ def make_default_user_service(repo: Repository):
|
|
47
45
|
if "metadata" in data:
|
48
46
|
data["extra"] = data.pop("metadata")
|
49
47
|
|
50
|
-
#
|
48
|
+
# optional: protect email change too
|
51
49
|
email = data.get("email")
|
52
50
|
tenant_id = data.get("tenant_id")
|
53
51
|
if email is not None:
|
@@ -61,21 +59,34 @@ def make_default_user_service(repo: Repository):
|
|
61
59
|
async def _exists(session):
|
62
60
|
return await repo.exists(session, where=where)
|
63
61
|
|
64
|
-
data["
|
65
|
-
|
62
|
+
data["_preupdate_exists_check"] = _exists
|
66
63
|
return data
|
67
64
|
|
68
65
|
class _Svc(ServiceWithHooks):
|
69
66
|
async def create(self, session, data):
|
67
|
+
# IMPORTANT: run pre_create first
|
68
|
+
data = await self.pre_create(data)
|
70
69
|
exists_cb = data.pop("_precreate_exists_check", None)
|
71
70
|
if exists_cb and await exists_cb(session):
|
72
71
|
raise HTTPException(status_code=409, detail="User with this email already exists.")
|
73
|
-
|
72
|
+
try:
|
73
|
+
return await self.repo.create(session, data)
|
74
|
+
except IntegrityError as e:
|
75
|
+
# race-safety / fallback
|
76
|
+
if "uq_users_tenant_id" in str(e.orig) or "ci_email" in str(e.orig):
|
77
|
+
raise HTTPException(status_code=409, detail="User with this email already exists.") from e
|
78
|
+
raise
|
74
79
|
|
75
80
|
async def update(self, session, id_value, data):
|
76
|
-
|
81
|
+
data = await self.pre_update(data)
|
82
|
+
exists_cb = data.pop("_preupdate_exists_check", None)
|
77
83
|
if exists_cb and await exists_cb(session):
|
78
84
|
raise HTTPException(status_code=409, detail="User with this email already exists.")
|
79
|
-
|
85
|
+
try:
|
86
|
+
return await self.repo.update(session, id_value, data)
|
87
|
+
except IntegrityError as e:
|
88
|
+
if "uq_users_tenant_id" in str(e.orig) or "ci_email" in str(e.orig):
|
89
|
+
raise HTTPException(status_code=409, detail="User with this email already exists.") from e
|
90
|
+
raise
|
80
91
|
|
81
92
|
return _Svc(repo, pre_create=_user_pre_create, pre_update=_user_pre_update)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/__init__.py
RENAMED
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/catchall.py
RENAMED
File without changes
|
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/api/fastapi/middleware/errors/exceptions.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/models_schemas/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/db/templates/setup/script.py.mako.tmpl
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/templates/grafana_dashboard.json
RENAMED
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/templates/otel-collector.yaml
RENAMED
File without changes
|
{svc_infra-0.1.185 → svc_infra-0.1.186}/src/svc_infra/observability/templates/prometheus_rules.yml
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|