core-framework 0.12.9__tar.gz → 0.12.10__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.
- {core_framework-0.12.9 → core_framework-0.12.10}/PKG-INFO +1 -1
- {core_framework-0.12.9 → core_framework-0.12.10}/core/__init__.py +10 -1
- {core_framework-0.12.9 → core_framework-0.12.10}/core/app.py +51 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/views.py +99 -23
- {core_framework-0.12.9 → core_framework-0.12.10}/core/config.py +20 -0
- core_framework-0.12.10/core/validation.py +768 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/views.py +108 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/pyproject.toml +1 -1
- core_framework-0.12.10/tests/test_validation.py +307 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/.gitignore +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/README.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/backends.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/base.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/decorators.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/hashers.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/helpers.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/middleware.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/models.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/permissions.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/schemas.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/auth/tokens.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/choices.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/cli/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/cli/main.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/database.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/datetime.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/dependencies.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/deployment/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/deployment/docker.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/deployment/kubernetes.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/deployment/pm2.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/exceptions.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/fields.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/avro.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/base.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/config.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/confluent/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/confluent/consumer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/confluent/producer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/decorators.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/kafka/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/kafka/admin.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/kafka/broker.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/kafka/consumer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/kafka/producer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/rabbitmq/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/rabbitmq/broker.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/rabbitmq/consumer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/rabbitmq/producer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/redis/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/redis/broker.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/redis/consumer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/redis/producer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/registry.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/topics.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/messaging/workers.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/middleware.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/analyzer.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/cli.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/engine.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/migration.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/operations.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/migrations/state.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/models.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/permissions.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/querysets.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/relations.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/routing.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/serializers.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/base.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/config.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/decorators.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/registry.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/scheduler.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tasks/worker.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/tenancy.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/assertions.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/client.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/database.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/factories.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/mocks.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/testing/plugin.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/core/validators.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/01-quickstart.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/02-viewsets.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/03-authentication.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/04-messaging.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/05-multi-service.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/06-tasks.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/07-deployment.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/08-complete-example.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/09-settings.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/10-migrations.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/11-permissions.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/12-auth-backends.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/13-validators.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/14-querysets.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/15-routing.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/16-serializers.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/17-datetime.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/18-dependencies.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/19-views.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/20-fields.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/21-tenancy.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/22-replicas.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/23-soft-delete.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/24-relations.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/25-exceptions.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/26-choices.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/27-workers.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/28-avro.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/29-topics.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/30-changelog-0.12.2.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/31-middleware.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/32-migration-guide-0.12.2.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/32-testing.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/33-changelog-0.12.3.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/99-faq-troubleshooting.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/GUIDE.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/docs/README.md +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/example/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/example/app.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/example/auth.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/example/models.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/example/schemas.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/example/views.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/libs/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/main.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/__init__.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/conftest.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/test_auth_helpers.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/test_imports.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/test_models.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/test_permissions.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/test_querysets.py +0 -0
- {core_framework-0.12.9 → core_framework-0.12.10}/tests/test_serializers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.10
|
|
4
4
|
Summary: Core Framework - Django-inspired, FastAPI-powered. Alta performance, baixo acoplamento, produtividade extrema.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SorPuti/core-framework
|
|
6
6
|
Project-URL: Documentation, https://github.com/SorPuti/core-framework#readme
|
|
@@ -41,6 +41,15 @@ from core.dependencies import Depends, get_db, get_current_user
|
|
|
41
41
|
from core.config import Settings, get_settings
|
|
42
42
|
from core.app import CoreApp
|
|
43
43
|
|
|
44
|
+
# Validation
|
|
45
|
+
from core.validation import (
|
|
46
|
+
SchemaModelValidator,
|
|
47
|
+
SchemaModelMismatchError,
|
|
48
|
+
ValidationWarning,
|
|
49
|
+
validate_schema,
|
|
50
|
+
validate_all_viewsets,
|
|
51
|
+
)
|
|
52
|
+
|
|
44
53
|
# Advanced Fields (UUID7, JSON, etc.)
|
|
45
54
|
from core.fields import (
|
|
46
55
|
uuid7,
|
|
@@ -278,7 +287,7 @@ from core.exceptions import (
|
|
|
278
287
|
MissingDependency,
|
|
279
288
|
)
|
|
280
289
|
|
|
281
|
-
__version__ = "0.12.
|
|
290
|
+
__version__ = "0.12.10"
|
|
282
291
|
__all__ = [
|
|
283
292
|
# Models
|
|
284
293
|
"Model",
|
|
@@ -158,6 +158,13 @@ class CoreApp:
|
|
|
158
158
|
|
|
159
159
|
async def _startup(self) -> None:
|
|
160
160
|
"""Executa tarefas de startup."""
|
|
161
|
+
import logging
|
|
162
|
+
logger = logging.getLogger("core.app")
|
|
163
|
+
|
|
164
|
+
# Schema/Model validation (before database init for fail-fast)
|
|
165
|
+
if getattr(self.settings, "strict_validation", self.settings.debug):
|
|
166
|
+
await self._validate_schemas()
|
|
167
|
+
|
|
161
168
|
# Verifica se deve usar replicas
|
|
162
169
|
if self.settings.has_read_replica:
|
|
163
170
|
# Inicializa com read/write replicas
|
|
@@ -195,6 +202,50 @@ class CoreApp:
|
|
|
195
202
|
if hasattr(result, "__await__"):
|
|
196
203
|
await result
|
|
197
204
|
|
|
205
|
+
async def _validate_schemas(self) -> None:
|
|
206
|
+
"""
|
|
207
|
+
Validate all ViewSet schemas against their models.
|
|
208
|
+
|
|
209
|
+
Called during startup if strict_validation is enabled.
|
|
210
|
+
In DEBUG mode, raises SchemaModelMismatchError on critical issues.
|
|
211
|
+
In production, logs errors but continues.
|
|
212
|
+
"""
|
|
213
|
+
import logging
|
|
214
|
+
logger = logging.getLogger("core.app")
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
from core.views import validate_pending_viewsets
|
|
218
|
+
from core.validation import SchemaModelMismatchError
|
|
219
|
+
|
|
220
|
+
logger.info("Running schema/model validations...")
|
|
221
|
+
|
|
222
|
+
# In debug mode, fail fast on critical issues
|
|
223
|
+
strict = self.settings.debug
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
issues = validate_pending_viewsets(strict=strict)
|
|
227
|
+
|
|
228
|
+
if issues:
|
|
229
|
+
logger.warning(
|
|
230
|
+
f"Schema validation completed with {len(issues)} issues"
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
logger.info("Schema validation passed")
|
|
234
|
+
|
|
235
|
+
except SchemaModelMismatchError as e:
|
|
236
|
+
if self.settings.debug:
|
|
237
|
+
logger.error(f"Schema validation failed: {e}")
|
|
238
|
+
raise RuntimeError(
|
|
239
|
+
f"Schema validation errors (set DEBUG=False to skip):\n{e}"
|
|
240
|
+
) from e
|
|
241
|
+
else:
|
|
242
|
+
logger.error(f"Schema validation errors (ignored): {e}")
|
|
243
|
+
|
|
244
|
+
except ImportError:
|
|
245
|
+
logger.debug("Validation module not available, skipping")
|
|
246
|
+
except Exception as e:
|
|
247
|
+
logger.warning(f"Could not validate schemas: {e}")
|
|
248
|
+
|
|
198
249
|
async def _shutdown(self) -> None:
|
|
199
250
|
"""Executa tarefas de shutdown."""
|
|
200
251
|
# Executa callbacks customizados
|
|
@@ -108,14 +108,22 @@ class AuthViewSet(ViewSet):
|
|
|
108
108
|
|
|
109
109
|
def _get_register_schema(self) -> type:
|
|
110
110
|
"""
|
|
111
|
-
|
|
111
|
+
Get registration schema with STRICT extra fields support.
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
This method validates against the model to determine if fields are
|
|
114
|
+
required (NOT NULL) or optional (nullable).
|
|
115
|
+
|
|
116
|
+
Rules:
|
|
117
|
+
- If model column is NOT NULL and has no default -> REQUIRED in schema
|
|
118
|
+
- If model column is nullable or has default -> OPTIONAL in schema
|
|
115
119
|
|
|
116
120
|
Returns:
|
|
117
121
|
Pydantic schema class for registration
|
|
118
122
|
"""
|
|
123
|
+
import logging
|
|
124
|
+
import warnings
|
|
125
|
+
logger = logging.getLogger("core.auth")
|
|
126
|
+
|
|
119
127
|
# If register_schema was explicitly overridden, use it
|
|
120
128
|
if self.register_schema != BaseRegisterInput:
|
|
121
129
|
return self.register_schema
|
|
@@ -124,32 +132,53 @@ class AuthViewSet(ViewSet):
|
|
|
124
132
|
if not self.extra_register_fields:
|
|
125
133
|
return BaseRegisterInput
|
|
126
134
|
|
|
127
|
-
#
|
|
135
|
+
# Return cached schema if available
|
|
128
136
|
if self._dynamic_register_schema is not None:
|
|
129
137
|
return self._dynamic_register_schema
|
|
130
138
|
|
|
131
|
-
# Build extra fields - all as optional strings by default
|
|
132
|
-
# Users can provide type hints via annotations in User model
|
|
133
|
-
extra_fields = {}
|
|
134
139
|
User = self._get_user_model()
|
|
135
|
-
|
|
140
|
+
|
|
141
|
+
# Get model column info for nullable/required check
|
|
142
|
+
model_columns = {}
|
|
143
|
+
try:
|
|
144
|
+
from sqlalchemy import inspect
|
|
145
|
+
mapper = inspect(User)
|
|
146
|
+
model_columns = {col.name: col for col in mapper.columns}
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.debug(f"Could not inspect User model: {e}")
|
|
149
|
+
|
|
150
|
+
extra_fields = {}
|
|
136
151
|
|
|
137
152
|
for field_name in self.extra_register_fields:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
col = model_columns.get(field_name)
|
|
154
|
+
|
|
155
|
+
if col is not None:
|
|
156
|
+
# Determine type from model
|
|
157
|
+
python_type = self._get_python_type_from_column(col.type)
|
|
158
|
+
|
|
159
|
+
# Check if field is required
|
|
160
|
+
is_nullable = col.nullable
|
|
161
|
+
has_default = col.default is not None or col.server_default is not None
|
|
162
|
+
|
|
163
|
+
if not is_nullable and not has_default:
|
|
164
|
+
# REQUIRED field - use ... (Ellipsis) as default
|
|
165
|
+
extra_fields[field_name] = (python_type, ...)
|
|
166
|
+
logger.info(
|
|
167
|
+
f"Field '{field_name}' is NOT NULL in model, "
|
|
168
|
+
f"adding as REQUIRED to register schema"
|
|
169
|
+
)
|
|
150
170
|
else:
|
|
151
|
-
|
|
171
|
+
# Optional field
|
|
172
|
+
extra_fields[field_name] = (python_type | None, None)
|
|
152
173
|
else:
|
|
174
|
+
# Field not in model columns, warn and make optional
|
|
175
|
+
warnings.warn(
|
|
176
|
+
f"Field '{field_name}' in extra_register_fields "
|
|
177
|
+
f"not found in {User.__name__} model columns. "
|
|
178
|
+
f"Adding as optional str.",
|
|
179
|
+
UserWarning,
|
|
180
|
+
stacklevel=2,
|
|
181
|
+
)
|
|
153
182
|
extra_fields[field_name] = (str | None, None)
|
|
154
183
|
|
|
155
184
|
# Create dynamic model
|
|
@@ -160,14 +189,61 @@ class AuthViewSet(ViewSet):
|
|
|
160
189
|
**extra_fields,
|
|
161
190
|
)
|
|
162
191
|
|
|
163
|
-
# Allow extra fields
|
|
192
|
+
# Allow extra fields (ignore unknown)
|
|
164
193
|
self._dynamic_register_schema.model_config = {
|
|
165
194
|
**BaseRegisterInput.model_config,
|
|
166
|
-
"extra": "ignore",
|
|
195
|
+
"extra": "ignore",
|
|
167
196
|
}
|
|
168
197
|
|
|
169
198
|
return self._dynamic_register_schema
|
|
170
199
|
|
|
200
|
+
def _get_python_type_from_column(self, sa_type) -> type:
|
|
201
|
+
"""
|
|
202
|
+
Convert SQLAlchemy column type to Python type.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
sa_type: SQLAlchemy type instance
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Corresponding Python type
|
|
209
|
+
"""
|
|
210
|
+
from sqlalchemy import String, Integer, Boolean, Float, Text, DateTime, Date
|
|
211
|
+
|
|
212
|
+
type_map = {
|
|
213
|
+
String: str,
|
|
214
|
+
Text: str,
|
|
215
|
+
Integer: int,
|
|
216
|
+
Boolean: bool,
|
|
217
|
+
Float: float,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for sa_cls, py_type in type_map.items():
|
|
221
|
+
if isinstance(sa_type, sa_cls):
|
|
222
|
+
return py_type
|
|
223
|
+
|
|
224
|
+
# Check type name for dialect-specific types
|
|
225
|
+
type_name = type(sa_type).__name__
|
|
226
|
+
if "String" in type_name or "Text" in type_name or "VARCHAR" in type_name:
|
|
227
|
+
return str
|
|
228
|
+
if "Integer" in type_name or "INT" in type_name:
|
|
229
|
+
return int
|
|
230
|
+
if "Boolean" in type_name or "BOOL" in type_name:
|
|
231
|
+
return bool
|
|
232
|
+
if "Float" in type_name or "Numeric" in type_name or "Decimal" in type_name:
|
|
233
|
+
return float
|
|
234
|
+
if "DateTime" in type_name or "Timestamp" in type_name:
|
|
235
|
+
from datetime import datetime
|
|
236
|
+
return datetime
|
|
237
|
+
if "Date" in type_name:
|
|
238
|
+
from datetime import date
|
|
239
|
+
return date
|
|
240
|
+
if "UUID" in type_name:
|
|
241
|
+
from uuid import UUID
|
|
242
|
+
return UUID
|
|
243
|
+
|
|
244
|
+
# Default to str
|
|
245
|
+
return str
|
|
246
|
+
|
|
171
247
|
def _create_tokens(self, user) -> dict:
|
|
172
248
|
"""
|
|
173
249
|
Bug #6 Fix: Create access and refresh tokens using current API.
|
|
@@ -76,6 +76,26 @@ class Settings(BaseSettings):
|
|
|
76
76
|
description="Chave secreta para criptografia e tokens",
|
|
77
77
|
)
|
|
78
78
|
|
|
79
|
+
# =========================================================================
|
|
80
|
+
# Validation
|
|
81
|
+
# =========================================================================
|
|
82
|
+
|
|
83
|
+
strict_validation: bool = PydanticField(
|
|
84
|
+
default=True,
|
|
85
|
+
description=(
|
|
86
|
+
"Habilita validação rigorosa de schemas contra models. "
|
|
87
|
+
"Em modo strict, erros críticos (campo NOT NULL opcional no schema) "
|
|
88
|
+
"causam falha no startup em DEBUG mode."
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
validation_fail_fast: bool | None = PydanticField(
|
|
92
|
+
default=None,
|
|
93
|
+
description=(
|
|
94
|
+
"Se True, falha no primeiro erro de validação. "
|
|
95
|
+
"Se None, usa valor de DEBUG."
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
|
|
79
99
|
# =========================================================================
|
|
80
100
|
# Database
|
|
81
101
|
# =========================================================================
|