pycharter 0.0.20__py3-none-any.whl → 0.0.22__py3-none-any.whl
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.
- api/dependencies/__init__.py +2 -1
- api/dependencies/database.py +71 -5
- api/main.py +47 -8
- api/models/contracts.py +6 -4
- api/models/metadata.py +11 -7
- api/models/schemas.py +16 -10
- api/routes/v1/contracts.py +498 -226
- api/routes/v1/metadata.py +52 -211
- api/routes/v1/schemas.py +1 -1
- api/routes/v1/settings.py +88 -1
- api/utils.py +224 -0
- pycharter/__init__.py +149 -93
- pycharter/data/templates/template_transform_advanced.yaml +50 -0
- pycharter/data/templates/template_transform_simple.yaml +59 -0
- pycharter/db/models/base.py +1 -2
- pycharter/etl_generator/orchestrator.py +463 -487
- pycharter/metadata_store/postgres.py +16 -191
- pycharter/metadata_store/sqlite.py +12 -41
- {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/METADATA +284 -62
- pycharter-0.0.22.dist-info/RECORD +358 -0
- ui/static/404/index.html +1 -1
- ui/static/404.html +1 -1
- ui/static/__next.__PAGE__.txt +1 -1
- ui/static/__next._full.txt +2 -2
- ui/static/__next._head.txt +1 -1
- ui/static/__next._index.txt +2 -2
- ui/static/__next._tree.txt +2 -2
- ui/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
- ui/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
- ui/static/_next/static/chunks/c4fa4f4114b7c352.js +1 -0
- ui/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
- ui/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- ui/static/_not-found/__next._full.txt +2 -2
- ui/static/_not-found/__next._head.txt +1 -1
- ui/static/_not-found/__next._index.txt +2 -2
- ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/_not-found/__next._not-found.txt +1 -1
- ui/static/_not-found/__next._tree.txt +2 -2
- ui/static/_not-found/index.html +1 -1
- ui/static/_not-found/index.txt +2 -2
- ui/static/contracts/__next._full.txt +3 -3
- ui/static/contracts/__next._head.txt +1 -1
- ui/static/contracts/__next._index.txt +2 -2
- ui/static/contracts/__next._tree.txt +2 -2
- ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/contracts/__next.contracts.txt +1 -1
- ui/static/contracts/index.html +1 -1
- ui/static/contracts/index.txt +3 -3
- ui/static/documentation/__next._full.txt +3 -3
- ui/static/documentation/__next._head.txt +1 -1
- ui/static/documentation/__next._index.txt +2 -2
- ui/static/documentation/__next._tree.txt +2 -2
- ui/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- ui/static/documentation/__next.documentation.txt +1 -1
- ui/static/documentation/index.html +2 -2
- ui/static/documentation/index.txt +3 -3
- ui/static/index.html +1 -1
- ui/static/index.txt +2 -2
- ui/static/metadata/__next._full.txt +2 -2
- ui/static/metadata/__next._head.txt +1 -1
- ui/static/metadata/__next._index.txt +2 -2
- ui/static/metadata/__next._tree.txt +2 -2
- ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/metadata/__next.metadata.txt +1 -1
- ui/static/metadata/index.html +1 -1
- ui/static/metadata/index.txt +2 -2
- ui/static/quality/__next._full.txt +2 -2
- ui/static/quality/__next._head.txt +1 -1
- ui/static/quality/__next._index.txt +2 -2
- ui/static/quality/__next._tree.txt +2 -2
- ui/static/quality/__next.quality.__PAGE__.txt +1 -1
- ui/static/quality/__next.quality.txt +1 -1
- ui/static/quality/index.html +2 -2
- ui/static/quality/index.txt +2 -2
- ui/static/rules/__next._full.txt +2 -2
- ui/static/rules/__next._head.txt +1 -1
- ui/static/rules/__next._index.txt +2 -2
- ui/static/rules/__next._tree.txt +2 -2
- ui/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/rules/__next.rules.txt +1 -1
- ui/static/rules/index.html +1 -1
- ui/static/rules/index.txt +2 -2
- ui/static/schemas/__next._full.txt +2 -2
- ui/static/schemas/__next._head.txt +1 -1
- ui/static/schemas/__next._index.txt +2 -2
- ui/static/schemas/__next._tree.txt +2 -2
- ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/schemas/__next.schemas.txt +1 -1
- ui/static/schemas/index.html +1 -1
- ui/static/schemas/index.txt +2 -2
- ui/static/settings/__next._full.txt +2 -2
- ui/static/settings/__next._head.txt +1 -1
- ui/static/settings/__next._index.txt +2 -2
- ui/static/settings/__next._tree.txt +2 -2
- ui/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/settings/__next.settings.txt +1 -1
- ui/static/settings/index.html +1 -1
- ui/static/settings/index.txt +2 -2
- ui/static/static/.gitkeep +0 -0
- ui/static/static/404/index.html +1 -0
- ui/static/static/404.html +1 -0
- ui/static/static/__next.__PAGE__.txt +10 -0
- ui/static/static/__next._full.txt +30 -0
- ui/static/static/__next._head.txt +7 -0
- ui/static/static/__next._index.txt +9 -0
- ui/static/static/__next._tree.txt +2 -0
- ui/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- ui/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- ui/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- ui/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
- ui/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- ui/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
- ui/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +1 -0
- ui/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- ui/static/static/_next/static/chunks/75d88a058d8ffaa6.js +1 -0
- ui/static/static/_next/static/chunks/8c89634cf6bad76f.js +1 -0
- ui/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
- ui/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- ui/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- ui/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- ui/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
- ui/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- ui/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
- ui/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- ui/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- ui/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- ui/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- ui/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- ui/static/static/_not-found/__next._full.txt +17 -0
- ui/static/static/_not-found/__next._head.txt +7 -0
- ui/static/static/_not-found/__next._index.txt +9 -0
- ui/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- ui/static/static/_not-found/__next._not-found.txt +4 -0
- ui/static/static/_not-found/__next._tree.txt +2 -0
- ui/static/static/_not-found/index.html +1 -0
- ui/static/static/_not-found/index.txt +17 -0
- ui/static/static/contracts/__next._full.txt +21 -0
- ui/static/static/contracts/__next._head.txt +7 -0
- ui/static/static/contracts/__next._index.txt +9 -0
- ui/static/static/contracts/__next._tree.txt +2 -0
- ui/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- ui/static/static/contracts/__next.contracts.txt +4 -0
- ui/static/static/contracts/index.html +1 -0
- ui/static/static/contracts/index.txt +21 -0
- ui/static/static/documentation/__next._full.txt +21 -0
- ui/static/static/documentation/__next._head.txt +7 -0
- ui/static/static/documentation/__next._index.txt +9 -0
- ui/static/static/documentation/__next._tree.txt +2 -0
- ui/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- ui/static/static/documentation/__next.documentation.txt +4 -0
- ui/static/static/documentation/index.html +93 -0
- ui/static/static/documentation/index.txt +21 -0
- ui/static/static/index.html +1 -0
- ui/static/static/index.txt +30 -0
- ui/static/static/metadata/__next._full.txt +21 -0
- ui/static/static/metadata/__next._head.txt +7 -0
- ui/static/static/metadata/__next._index.txt +9 -0
- ui/static/static/metadata/__next._tree.txt +2 -0
- ui/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- ui/static/static/metadata/__next.metadata.txt +4 -0
- ui/static/static/metadata/index.html +1 -0
- ui/static/static/metadata/index.txt +21 -0
- ui/static/static/quality/__next._full.txt +21 -0
- ui/static/static/quality/__next._head.txt +7 -0
- ui/static/static/quality/__next._index.txt +9 -0
- ui/static/static/quality/__next._tree.txt +2 -0
- ui/static/static/quality/__next.quality.__PAGE__.txt +9 -0
- ui/static/static/quality/__next.quality.txt +4 -0
- ui/static/static/quality/index.html +2 -0
- ui/static/static/quality/index.txt +21 -0
- ui/static/static/rules/__next._full.txt +21 -0
- ui/static/static/rules/__next._head.txt +7 -0
- ui/static/static/rules/__next._index.txt +9 -0
- ui/static/static/rules/__next._tree.txt +2 -0
- ui/static/static/rules/__next.rules.__PAGE__.txt +9 -0
- ui/static/static/rules/__next.rules.txt +4 -0
- ui/static/static/rules/index.html +1 -0
- ui/static/static/rules/index.txt +21 -0
- ui/static/static/schemas/__next._full.txt +21 -0
- ui/static/static/schemas/__next._head.txt +7 -0
- ui/static/static/schemas/__next._index.txt +9 -0
- ui/static/static/schemas/__next._tree.txt +2 -0
- ui/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- ui/static/static/schemas/__next.schemas.txt +4 -0
- ui/static/static/schemas/index.html +1 -0
- ui/static/static/schemas/index.txt +21 -0
- ui/static/static/settings/__next._full.txt +21 -0
- ui/static/static/settings/__next._head.txt +7 -0
- ui/static/static/settings/__next._index.txt +9 -0
- ui/static/static/settings/__next._tree.txt +2 -0
- ui/static/static/settings/__next.settings.__PAGE__.txt +9 -0
- ui/static/static/settings/__next.settings.txt +4 -0
- ui/static/static/settings/index.html +1 -0
- ui/static/static/settings/index.txt +21 -0
- ui/static/static/validation/__next._full.txt +21 -0
- ui/static/static/validation/__next._head.txt +7 -0
- ui/static/static/validation/__next._index.txt +9 -0
- ui/static/static/validation/__next._tree.txt +2 -0
- ui/static/static/validation/__next.validation.__PAGE__.txt +9 -0
- ui/static/static/validation/__next.validation.txt +4 -0
- ui/static/static/validation/index.html +1 -0
- ui/static/static/validation/index.txt +21 -0
- ui/static/validation/__next._full.txt +2 -2
- ui/static/validation/__next._head.txt +1 -1
- ui/static/validation/__next._index.txt +2 -2
- ui/static/validation/__next._tree.txt +2 -2
- ui/static/validation/__next.validation.__PAGE__.txt +1 -1
- ui/static/validation/__next.validation.txt +1 -1
- ui/static/validation/index.html +1 -1
- ui/static/validation/index.txt +2 -2
- pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +0 -160
- pycharter-0.0.20.dist-info/RECORD +0 -247
- {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/WHEEL +0 -0
- {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/licenses/LICENSE +0 -0
- {pycharter-0.0.20.dist-info → pycharter-0.0.22.dist-info}/top_level.txt +0 -0
- /ui/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- /ui/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
- /ui/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
api/dependencies/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ Dependencies for API routes.
|
|
|
4
4
|
This module provides FastAPI dependency injection for shared resources.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from api.dependencies.database import get_db_session
|
|
7
8
|
from api.dependencies.store import get_metadata_store
|
|
8
9
|
|
|
9
|
-
__all__ = ["get_metadata_store"]
|
|
10
|
+
__all__ = ["get_db_session", "get_metadata_store"]
|
api/dependencies/database.py
CHANGED
|
@@ -4,7 +4,7 @@ Database session dependency for API routes.
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Optional
|
|
7
|
+
from typing import Generator, Optional
|
|
8
8
|
|
|
9
9
|
from fastapi import Depends, HTTPException, status
|
|
10
10
|
from sqlalchemy import create_engine, inspect
|
|
@@ -99,36 +99,102 @@ def _ensure_sqlite_initialized(db_url: str) -> None:
|
|
|
99
99
|
logger.warning(f"Could not auto-initialize SQLite database: {e}")
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
def get_db_session() -> Session:
|
|
102
|
+
def get_db_session() -> Generator[Session, None, None]:
|
|
103
103
|
"""
|
|
104
104
|
FastAPI dependency to get database session.
|
|
105
105
|
|
|
106
106
|
Defaults to SQLite (sqlite:///pycharter.db) if no database URL is configured.
|
|
107
107
|
Automatically initializes SQLite database if it doesn't exist or is uninitialized.
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
**Important**: This dependency properly manages session lifecycle using yield,
|
|
110
|
+
ensuring sessions are closed after request completion.
|
|
111
|
+
|
|
112
|
+
Yields:
|
|
110
113
|
SQLAlchemy session
|
|
111
114
|
|
|
112
115
|
Raises:
|
|
113
116
|
HTTPException: If database connection fails
|
|
114
117
|
"""
|
|
118
|
+
import logging
|
|
119
|
+
|
|
120
|
+
logger = logging.getLogger(__name__)
|
|
121
|
+
|
|
115
122
|
db_url = get_database_url()
|
|
116
123
|
|
|
117
124
|
# Default to SQLite if no database URL is configured
|
|
118
125
|
if not db_url:
|
|
119
126
|
default_db_path = Path.cwd() / "pycharter.db"
|
|
120
127
|
db_url = f"sqlite:///{default_db_path}"
|
|
128
|
+
logger.warning(
|
|
129
|
+
f"No database URL configured. Using default SQLite: {db_url}\n"
|
|
130
|
+
f"To use PostgreSQL, set PYCHARTER_DATABASE_URL environment variable:\n"
|
|
131
|
+
f" export PYCHARTER_DATABASE_URL='postgresql://user:password@localhost:5432/pycharter'"
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
# Mask password in logs
|
|
135
|
+
masked_url = db_url
|
|
136
|
+
if "@" in db_url and "://" in db_url:
|
|
137
|
+
parts = db_url.split("@", 1)
|
|
138
|
+
if ":" in parts[0]:
|
|
139
|
+
user_pass = parts[0].split("://", 1)[1]
|
|
140
|
+
if ":" in user_pass:
|
|
141
|
+
user, _ = user_pass.split(":", 1)
|
|
142
|
+
masked_url = db_url.split(":", 2)[0] + "://" + user + ":****@" + parts[1]
|
|
143
|
+
logger.info(f"Using database: {masked_url}")
|
|
121
144
|
|
|
122
145
|
# Auto-initialize SQLite if needed
|
|
123
146
|
_ensure_sqlite_initialized(db_url)
|
|
124
147
|
|
|
148
|
+
session = None
|
|
125
149
|
try:
|
|
126
150
|
session = get_session(db_url)
|
|
127
|
-
|
|
151
|
+
# Test the connection by executing a simple query
|
|
152
|
+
from sqlalchemy import text
|
|
153
|
+
session.execute(text("SELECT 1"))
|
|
154
|
+
yield session
|
|
128
155
|
except Exception as e:
|
|
156
|
+
if session:
|
|
157
|
+
try:
|
|
158
|
+
session.rollback()
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
logger.error(f"Database session error: {e}", exc_info=True)
|
|
162
|
+
|
|
163
|
+
# Provide more helpful error messages
|
|
164
|
+
error_detail = "Failed to connect to database"
|
|
165
|
+
error_msg = str(e).lower()
|
|
166
|
+
|
|
167
|
+
# Check for common issues
|
|
168
|
+
if "no such table" in error_msg or "table" in error_msg and "doesn't exist" in error_msg:
|
|
169
|
+
error_detail = (
|
|
170
|
+
"Database tables not found. Please initialize the database:\n"
|
|
171
|
+
f" pycharter db init {db_url}"
|
|
172
|
+
)
|
|
173
|
+
elif "database is locked" in error_msg:
|
|
174
|
+
error_detail = (
|
|
175
|
+
"Database is locked. Another process may be using it.\n"
|
|
176
|
+
"Close other connections or wait a moment and try again."
|
|
177
|
+
)
|
|
178
|
+
elif "permission denied" in error_msg or "access denied" in error_msg:
|
|
179
|
+
error_detail = (
|
|
180
|
+
"Database permission denied. Check file permissions:\n"
|
|
181
|
+
f" chmod 644 {db_url.split(':///')[-1] if 'sqlite' in db_url else 'database file'}"
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
# Show detailed error in development mode
|
|
185
|
+
is_development = os.getenv("ENVIRONMENT") == "development" or not os.getenv("ENVIRONMENT")
|
|
186
|
+
if is_development:
|
|
187
|
+
error_detail = f"Failed to connect to database: {str(e)}"
|
|
188
|
+
|
|
129
189
|
raise HTTPException(
|
|
130
190
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
131
|
-
detail=
|
|
191
|
+
detail=error_detail,
|
|
132
192
|
)
|
|
193
|
+
finally:
|
|
194
|
+
if session:
|
|
195
|
+
try:
|
|
196
|
+
session.close()
|
|
197
|
+
except Exception:
|
|
198
|
+
pass
|
|
133
199
|
|
|
134
200
|
|
api/main.py
CHANGED
|
@@ -59,11 +59,35 @@ def create_application() -> FastAPI:
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
# Add CORS middleware
|
|
62
|
+
import os
|
|
63
|
+
# Get CORS origins from environment variable
|
|
64
|
+
cors_origins_env = os.getenv("CORS_ORIGINS", "").strip()
|
|
65
|
+
cors_origins = [origin.strip() for origin in cors_origins_env.split(",") if origin.strip()] if cors_origins_env else []
|
|
66
|
+
|
|
67
|
+
# Default behavior: In development (or when ENVIRONMENT not set), allow localhost origins
|
|
68
|
+
# This makes development easier without requiring environment variables
|
|
69
|
+
is_development = os.getenv("ENVIRONMENT") == "development"
|
|
70
|
+
is_production = os.getenv("ENVIRONMENT") == "production"
|
|
71
|
+
|
|
72
|
+
# If no explicit CORS origins set and not in production, allow localhost for development
|
|
73
|
+
if not cors_origins and not is_production:
|
|
74
|
+
cors_origins = [
|
|
75
|
+
"http://localhost:3000",
|
|
76
|
+
"http://localhost:3001",
|
|
77
|
+
"http://127.0.0.1:3000",
|
|
78
|
+
"http://127.0.0.1:3001",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
# Configure CORS middleware
|
|
82
|
+
# Note: Can't use allow_credentials=True with allow_origins=["*"]
|
|
83
|
+
# So we use specific origins for development, or "*" without credentials for production
|
|
84
|
+
use_credentials = bool(cors_origins and "*" not in cors_origins)
|
|
85
|
+
|
|
62
86
|
app.add_middleware(
|
|
63
87
|
CORSMiddleware,
|
|
64
|
-
allow_origins=["*"], #
|
|
65
|
-
allow_credentials=
|
|
66
|
-
allow_methods=["
|
|
88
|
+
allow_origins=cors_origins if cors_origins else ["*"], # Fallback to "*" if explicitly empty
|
|
89
|
+
allow_credentials=use_credentials,
|
|
90
|
+
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
67
91
|
allow_headers=["*"],
|
|
68
92
|
)
|
|
69
93
|
|
|
@@ -179,13 +203,28 @@ def create_application() -> FastAPI:
|
|
|
179
203
|
@app.exception_handler(Exception)
|
|
180
204
|
async def global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
181
205
|
"""Global exception handler for unhandled errors."""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
206
|
+
import logging
|
|
207
|
+
import os
|
|
208
|
+
|
|
209
|
+
logger = logging.getLogger(__name__)
|
|
210
|
+
logger.error(f"Unhandled exception: {exc}", exc_info=True)
|
|
211
|
+
|
|
212
|
+
# In production, don't expose internal error details
|
|
213
|
+
is_development = os.getenv("ENVIRONMENT") == "development"
|
|
214
|
+
|
|
215
|
+
error_detail = {
|
|
216
|
+
"error": "Internal server error",
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if is_development:
|
|
220
|
+
error_detail.update({
|
|
186
221
|
"message": str(exc),
|
|
187
222
|
"type": type(exc).__name__,
|
|
188
|
-
}
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
return JSONResponse(
|
|
226
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
227
|
+
content=error_detail,
|
|
189
228
|
)
|
|
190
229
|
|
|
191
230
|
# Override openapi() method to handle schema generation errors gracefully
|
api/models/contracts.py
CHANGED
|
@@ -4,7 +4,7 @@ Request/Response models for contract endpoints.
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Dict, List, Optional
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
8
8
|
|
|
9
9
|
from pycharter.shared.name_validator import validate_name
|
|
10
10
|
|
|
@@ -37,7 +37,7 @@ class ContractParseRequest(BaseModel):
|
|
|
37
37
|
class ContractParseResponse(BaseModel):
|
|
38
38
|
"""Response model for parsed contract."""
|
|
39
39
|
|
|
40
|
-
schema: Dict[str, Any] = Field(..., description="JSON Schema definition")
|
|
40
|
+
schema: Dict[str, Any] = Field(..., description="JSON Schema definition", alias="schema")
|
|
41
41
|
metadata: Optional[Dict[str, Any]] = Field(None, description="Metadata information")
|
|
42
42
|
ownership: Optional[Dict[str, Any]] = Field(None, description="Ownership information")
|
|
43
43
|
governance_rules: Optional[Dict[str, Any]] = Field(None, description="Governance rules")
|
|
@@ -45,8 +45,9 @@ class ContractParseResponse(BaseModel):
|
|
|
45
45
|
validation_rules: Optional[Dict[str, Any]] = Field(None, description="Validation rules")
|
|
46
46
|
versions: Optional[Dict[str, str]] = Field(None, description="Component versions")
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
model_config = ConfigDict(
|
|
49
|
+
populate_by_name=True,
|
|
50
|
+
json_schema_extra={
|
|
50
51
|
"example": {
|
|
51
52
|
"schema": {
|
|
52
53
|
"type": "object",
|
|
@@ -65,6 +66,7 @@ class ContractParseResponse(BaseModel):
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
}
|
|
69
|
+
)
|
|
68
70
|
|
|
69
71
|
|
|
70
72
|
class ContractBuildRequest(BaseModel):
|
api/models/metadata.py
CHANGED
|
@@ -4,7 +4,7 @@ Request/Response models for metadata store endpoints.
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Dict, List, Optional
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel, Field, field_validator
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
8
8
|
|
|
9
9
|
from pycharter.shared.name_validator import validate_name
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ class SchemaStoreRequest(BaseModel):
|
|
|
13
13
|
"""Request model for storing a schema."""
|
|
14
14
|
|
|
15
15
|
schema_name: str = Field(..., description="Schema name/identifier")
|
|
16
|
-
schema: Dict[str, Any] = Field(..., description="JSON Schema definition")
|
|
16
|
+
schema: Dict[str, Any] = Field(..., description="JSON Schema definition", alias="schema")
|
|
17
17
|
version: str = Field(..., description="Schema version")
|
|
18
18
|
|
|
19
19
|
@field_validator('schema_name')
|
|
@@ -30,8 +30,9 @@ class SchemaStoreRequest(BaseModel):
|
|
|
30
30
|
v['title'] = validate_name(str(v['title']), field_name="schema.title")
|
|
31
31
|
return v
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
model_config = ConfigDict(
|
|
34
|
+
populate_by_name=True,
|
|
35
|
+
json_schema_extra={
|
|
35
36
|
"example": {
|
|
36
37
|
"schema_name": "user_schema",
|
|
37
38
|
"schema": {
|
|
@@ -44,6 +45,7 @@ class SchemaStoreRequest(BaseModel):
|
|
|
44
45
|
"version": "1.0.0"
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
)
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
class SchemaStoreResponse(BaseModel):
|
|
@@ -81,11 +83,12 @@ class SchemaGetRequest(BaseModel):
|
|
|
81
83
|
class SchemaGetResponse(BaseModel):
|
|
82
84
|
"""Response model for retrieved schema."""
|
|
83
85
|
|
|
84
|
-
schema: Dict[str, Any] = Field(..., description="JSON Schema definition")
|
|
86
|
+
schema: Dict[str, Any] = Field(..., description="JSON Schema definition", alias="schema")
|
|
85
87
|
version: Optional[str] = Field(None, description="Schema version")
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
model_config = ConfigDict(
|
|
90
|
+
populate_by_name=True,
|
|
91
|
+
json_schema_extra={
|
|
89
92
|
"example": {
|
|
90
93
|
"schema": {
|
|
91
94
|
"type": "object",
|
|
@@ -97,6 +100,7 @@ class SchemaGetResponse(BaseModel):
|
|
|
97
100
|
"version": "1.0.0"
|
|
98
101
|
}
|
|
99
102
|
}
|
|
103
|
+
)
|
|
100
104
|
|
|
101
105
|
|
|
102
106
|
class MetadataStoreRequest(BaseModel):
|
api/models/schemas.py
CHANGED
|
@@ -4,17 +4,18 @@ Request/Response models for schema generation and conversion endpoints.
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Dict, Optional
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class SchemaGenerateRequest(BaseModel):
|
|
11
11
|
"""Request model for generating a Pydantic model from JSON Schema."""
|
|
12
12
|
|
|
13
|
-
schema: Dict[str, Any] = Field(..., description="JSON Schema definition")
|
|
13
|
+
schema: Dict[str, Any] = Field(..., description="JSON Schema definition", alias="schema")
|
|
14
14
|
model_name: Optional[str] = Field("DynamicModel", description="Name for the generated model")
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
model_config = ConfigDict(
|
|
17
|
+
populate_by_name=True,
|
|
18
|
+
json_schema_extra={
|
|
18
19
|
"example": {
|
|
19
20
|
"schema": {
|
|
20
21
|
"type": "object",
|
|
@@ -27,17 +28,19 @@ class SchemaGenerateRequest(BaseModel):
|
|
|
27
28
|
"model_name": "User"
|
|
28
29
|
}
|
|
29
30
|
}
|
|
31
|
+
)
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
class SchemaGenerateResponse(BaseModel):
|
|
33
35
|
"""Response model for generated schema."""
|
|
34
36
|
|
|
35
37
|
model_name: str = Field(..., description="Name of the generated model")
|
|
36
|
-
|
|
38
|
+
schema_definition: Dict[str, Any] = Field(..., description="JSON Schema representation of the model", alias="schema_json")
|
|
37
39
|
message: str = Field(..., description="Success message")
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
model_config = ConfigDict(
|
|
42
|
+
populate_by_name=True,
|
|
43
|
+
json_schema_extra={
|
|
41
44
|
"example": {
|
|
42
45
|
"model_name": "User",
|
|
43
46
|
"schema_json": {
|
|
@@ -51,6 +54,7 @@ class SchemaGenerateResponse(BaseModel):
|
|
|
51
54
|
"message": "Model generated successfully"
|
|
52
55
|
}
|
|
53
56
|
}
|
|
57
|
+
)
|
|
54
58
|
|
|
55
59
|
|
|
56
60
|
class SchemaConvertRequest(BaseModel):
|
|
@@ -71,12 +75,13 @@ class SchemaConvertRequest(BaseModel):
|
|
|
71
75
|
class SchemaConvertResponse(BaseModel):
|
|
72
76
|
"""Response model for converted schema."""
|
|
73
77
|
|
|
74
|
-
schema: Dict[str, Any] = Field(..., description="JSON Schema definition")
|
|
78
|
+
schema: Dict[str, Any] = Field(..., description="JSON Schema definition", alias="schema")
|
|
75
79
|
title: Optional[str] = Field(None, description="Schema title")
|
|
76
80
|
version: Optional[str] = Field(None, description="Schema version")
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
model_config = ConfigDict(
|
|
83
|
+
populate_by_name=True,
|
|
84
|
+
json_schema_extra={
|
|
80
85
|
"example": {
|
|
81
86
|
"schema": {
|
|
82
87
|
"type": "object",
|
|
@@ -89,4 +94,5 @@ class SchemaConvertResponse(BaseModel):
|
|
|
89
94
|
"version": "1.0.0"
|
|
90
95
|
}
|
|
91
96
|
}
|
|
97
|
+
)
|
|
92
98
|
|