mocksql 0.1.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.
Files changed (79) hide show
  1. mocksql-0.1.0/PKG-INFO +232 -0
  2. mocksql-0.1.0/README.md +187 -0
  3. mocksql-0.1.0/app/__init__.py +0 -0
  4. mocksql-0.1.0/app/api/__init__.py +0 -0
  5. mocksql-0.1.0/app/api/endpoints/__init__.py +0 -0
  6. mocksql-0.1.0/app/api/endpoints/messages.py +114 -0
  7. mocksql-0.1.0/app/api/endpoints/models.py +117 -0
  8. mocksql-0.1.0/app/api/endpoints/projects.py +141 -0
  9. mocksql-0.1.0/app/api/endpoints/query.py +413 -0
  10. mocksql-0.1.0/app/api/endpoints/users.py +44 -0
  11. mocksql-0.1.0/app/exceptions/__init__.py +0 -0
  12. mocksql-0.1.0/app/exceptions/exceptions.py +4 -0
  13. mocksql-0.1.0/app/services/__init__.py +0 -0
  14. mocksql-0.1.0/app/services/query_service.py +2 -0
  15. mocksql-0.1.0/build_query/__init__.py +0 -0
  16. mocksql-0.1.0/build_query/constraint_simplifier.py +1066 -0
  17. mocksql-0.1.0/build_query/converstion_history.py +336 -0
  18. mocksql-0.1.0/build_query/examples_executor.py +870 -0
  19. mocksql-0.1.0/build_query/examples_generator.py +424 -0
  20. mocksql-0.1.0/build_query/other.py +62 -0
  21. mocksql-0.1.0/build_query/profile_checker.py +342 -0
  22. mocksql-0.1.0/build_query/profiler.py +2135 -0
  23. mocksql-0.1.0/build_query/prompt_tools.py +864 -0
  24. mocksql-0.1.0/build_query/query_chain.py +172 -0
  25. mocksql-0.1.0/build_query/query_executor.py +125 -0
  26. mocksql-0.1.0/build_query/routing.py +113 -0
  27. mocksql-0.1.0/build_query/schema_fetcher.py +138 -0
  28. mocksql-0.1.0/build_query/state.py +48 -0
  29. mocksql-0.1.0/build_query/test_evaluator.py +124 -0
  30. mocksql-0.1.0/build_query/validator.py +546 -0
  31. mocksql-0.1.0/cli/__init__.py +0 -0
  32. mocksql-0.1.0/cli/generate.py +388 -0
  33. mocksql-0.1.0/cli/main.py +321 -0
  34. mocksql-0.1.0/cli/test_runner.py +323 -0
  35. mocksql-0.1.0/common_vars.py +365 -0
  36. mocksql-0.1.0/fetch_secrets.py +34 -0
  37. mocksql-0.1.0/init/__init__.py +0 -0
  38. mocksql-0.1.0/init/add_column.py +153 -0
  39. mocksql-0.1.0/init/add_table.py +129 -0
  40. mocksql-0.1.0/init/create_user.py +72 -0
  41. mocksql-0.1.0/init/grant_access_to_db.py +31 -0
  42. mocksql-0.1.0/init/init_db.py +154 -0
  43. mocksql-0.1.0/models/__init__.py +0 -0
  44. mocksql-0.1.0/models/database.py +66 -0
  45. mocksql-0.1.0/models/db_pool.py +161 -0
  46. mocksql-0.1.0/models/env_variables.py +41 -0
  47. mocksql-0.1.0/models/message_service.py +286 -0
  48. mocksql-0.1.0/models/model.py +2 -0
  49. mocksql-0.1.0/models/model_service.py +2 -0
  50. mocksql-0.1.0/models/permissions.py +66 -0
  51. mocksql-0.1.0/models/schemas.py +122 -0
  52. mocksql-0.1.0/models/session_service.py +105 -0
  53. mocksql-0.1.0/models/user_service.py +99 -0
  54. mocksql-0.1.0/pyproject.toml +92 -0
  55. mocksql-0.1.0/server.py +95 -0
  56. mocksql-0.1.0/sql_functions/__init__.py +0 -0
  57. mocksql-0.1.0/sql_functions/functions.py +50 -0
  58. mocksql-0.1.0/sql_functions/helpers.py +276 -0
  59. mocksql-0.1.0/storage/__init__.py +0 -0
  60. mocksql-0.1.0/storage/config.py +47 -0
  61. mocksql-0.1.0/storage/test_repository.py +257 -0
  62. mocksql-0.1.0/utils/__init__.py +19 -0
  63. mocksql-0.1.0/utils/bigquery_test_helper.py +416 -0
  64. mocksql-0.1.0/utils/duckdb_test_helper.py +235 -0
  65. mocksql-0.1.0/utils/errors.py +376 -0
  66. mocksql-0.1.0/utils/examples.py +754 -0
  67. mocksql-0.1.0/utils/find_grains.py +822 -0
  68. mocksql-0.1.0/utils/insert_examples.py +366 -0
  69. mocksql-0.1.0/utils/llm_errors.py +25 -0
  70. mocksql-0.1.0/utils/logger.py +0 -0
  71. mocksql-0.1.0/utils/models.py +141 -0
  72. mocksql-0.1.0/utils/msg_types.py +25 -0
  73. mocksql-0.1.0/utils/postgres_db_utils.py +39 -0
  74. mocksql-0.1.0/utils/postgres_test_helper.py +178 -0
  75. mocksql-0.1.0/utils/prompt_utils.py +27 -0
  76. mocksql-0.1.0/utils/query_services.py +17 -0
  77. mocksql-0.1.0/utils/saver.py +270 -0
  78. mocksql-0.1.0/utils/schema_utils.py +125 -0
  79. mocksql-0.1.0/utils/sql_code.py +224 -0
mocksql-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.4
2
+ Name: mocksql
3
+ Version: 0.1.0
4
+ Summary: TDD engine for Analytics Engineering — generate unit test data for SQL queries
5
+ License: MIT
6
+ Keywords: sql,testing,analytics,bigquery,duckdb,langgraph
7
+ Author: Adel Skhiri
8
+ Author-email: skhiriadel92@gmail.com
9
+ Requires-Python: >=3.11,<3.14
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Requires-Dist: bcrypt (>=4.2.0,<5.0.0)
19
+ Requires-Dist: db-dtypes (>=1.2.0,<2.0.0)
20
+ Requires-Dist: duckdb (>=1.5.0,<2.0.0)
21
+ Requires-Dist: fastapi (>=0.115.0,<1.0.0)
22
+ Requires-Dist: google-cloud-bigquery (>=3.41.0,<4.0.0)
23
+ Requires-Dist: google-cloud-bigquery-storage (>=2.25.0,<3.0.0)
24
+ Requires-Dist: google-cloud-secret-manager (>=2.20.1,<3.0.0)
25
+ Requires-Dist: itsdangerous (>=2.2.0,<3.0.0)
26
+ Requires-Dist: langchain (>=1.2.0,<2.0.0)
27
+ Requires-Dist: langchain-classic (==1.0.3)
28
+ Requires-Dist: langchain-google-genai (>=4.0.0,<5.0.0)
29
+ Requires-Dist: langgraph (>=1.1.0,<2.0.0)
30
+ Requires-Dist: pandas (>=2.2.2,<3.0.0)
31
+ Requires-Dist: passlib (>=1.7.4,<2.0.0)
32
+ Requires-Dist: pyarrow (>=22,<23)
33
+ Requires-Dist: pydantic (>=2.0.0,<3.0.0)
34
+ Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
35
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
36
+ Requires-Dist: pyyaml (>=6.0)
37
+ Requires-Dist: sqlglot[c] (>=30.4.0,<31.0.0)
38
+ Requires-Dist: sqlmesh (>=0.234.0,<0.235.0)
39
+ Requires-Dist: typer[all] (>=0.24.2,<0.25.0)
40
+ Requires-Dist: uvicorn (>=0.44.0,<0.45.0)
41
+ Project-URL: Homepage, https://github.com/skadel/mocksql
42
+ Project-URL: Repository, https://github.com/skadel/mocksql
43
+ Description-Content-Type: text/markdown
44
+
45
+ # MockSQL — Backend
46
+
47
+ **FastAPI + LangGraph + CLI**
48
+
49
+ Ce dossier contient le cœur de MockSQL : l'API REST, le graph LangGraph de génération de données, le CLI, et le package `mocksql-ui` (serveur web + assets React).
50
+
51
+ ---
52
+
53
+ ## Quickstart local
54
+
55
+ > Prérequis : Python ≥ 3.11, Poetry ≥ 1.8, un projet Google Cloud avec la facturation activée.
56
+
57
+ ### 1. Authentification Google Cloud
58
+
59
+ ```bash
60
+ gcloud auth application-default login
61
+ gcloud config set project <PROJECT_ID>
62
+ ```
63
+
64
+ ### 2. Variables d'environnement
65
+
66
+ ```bash
67
+ cp .env.example .env # puis compléter les valeurs
68
+ ```
69
+
70
+ Variables minimales requises :
71
+
72
+ ```dotenv
73
+ PROJECT_ID=<votre-projet-gcp>
74
+ GOOGLE_CLOUD_PROJECT=<votre-projet-gcp>
75
+ BQ_SCHEMA_BILLING_PROJECT=<votre-projet-gcp>
76
+ DEFAULT_MODEL_NAME=gemini-2.0-flash-lite
77
+ DUCKDB_PATH=data/mocksql.duckdb
78
+ SECRET_KEY=<générer avec : python -c "import secrets; print(secrets.token_hex(32))">
79
+ API_SECRET_KEY=<idem>
80
+ FRONT_URL=http://127.0.0.1:3000
81
+ ```
82
+
83
+ ### 3. Installation & lancement
84
+
85
+ ```bash
86
+ python -m venv .venv
87
+
88
+ # Linux / macOS
89
+ source .venv/bin/activate
90
+
91
+ # Windows
92
+ .\.venv\Scripts\activate
93
+
94
+ pip install poetry && poetry install
95
+
96
+ # Lancer le serveur
97
+ uvicorn server:app --port 8080 --reload
98
+ ```
99
+
100
+ L'API est accessible sur **http://localhost:8080**, le frontend sur **http://localhost:3000** (voir [../front/README.md](../front/README.md)).
101
+
102
+ ---
103
+
104
+ ## Commandes de développement
105
+
106
+ ```bash
107
+ make style # ruff check + ruff format --check + vulture (code mort)
108
+ make format # ruff format + ruff check --fix (auto-correction)
109
+ make test # pytest
110
+ make check # style + test
111
+ ```
112
+
113
+ Type checking :
114
+
115
+ ```bash
116
+ poetry run mypy build_query/ app/
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Packaging
122
+
123
+ MockSQL produit deux wheels indépendants :
124
+
125
+ | Wheel | Contenu |
126
+ |-------|---------|
127
+ | `mocksql-*.whl` | CLI + LangGraph core (sans UI) |
128
+ | `mocksql_ui-*.whl` | Serveur web + assets React bundlés |
129
+
130
+ ### Builder les wheels
131
+
132
+ ```bash
133
+ # CLI uniquement
134
+ make build-cli
135
+
136
+ # CLI + UI (build React inclus — Node.js 18+ requis)
137
+ make build-ui
138
+ ```
139
+
140
+ Les wheels sont générés dans `dist/`.
141
+
142
+ ### Lancer l'UI depuis les wheels
143
+
144
+ ```bash
145
+ pip install dist/mocksql-*.whl dist/mocksql_ui-*.whl
146
+ mocksql ui # http://localhost:8080/static/
147
+ mocksql ui --port 4000
148
+ mocksql ui --no-browser
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Déploiement
154
+
155
+ ### Variables d'environnement (production)
156
+
157
+ Voir `.env.example` pour la liste complète. En production (Cloud Run), les secrets sont gérés via **Google Secret Manager** ou les variables d'environnement du service.
158
+
159
+ ### Conteneurisation (Docker)
160
+
161
+ ```bash
162
+ docker build -t mocksql-backend .
163
+ docker run -d -p 8080:8080 \
164
+ -e PROJECT_ID=<votre-projet-gcp> \
165
+ -e GOOGLE_APPLICATION_CREDENTIALS=/keys/sa.json \
166
+ -v ~/keys:/keys \
167
+ mocksql-backend
168
+ ```
169
+
170
+ ### Google Cloud Run
171
+
172
+ #### 1. Build & push
173
+
174
+ ```bash
175
+ gcloud builds submit --tag gcr.io/${PROJECT_ID}/mocksql-backend .
176
+ ```
177
+
178
+ #### 2. Secrets
179
+
180
+ ```bash
181
+ gcloud secrets create mocksql-env --data-file .env
182
+ ```
183
+
184
+ #### 3. Déploiement
185
+
186
+ ```bash
187
+ gcloud run deploy mocksql-backend \
188
+ --image gcr.io/${PROJECT_ID}/mocksql-backend \
189
+ --region europe-west1 \
190
+ --platform managed \
191
+ --allow-unauthenticated \
192
+ --memory 1Gi \
193
+ --service-account mocksql-sa@${PROJECT_ID}.iam.gserviceaccount.com
194
+ ```
195
+
196
+ ### Infrastructure Terraform
197
+
198
+ ```bash
199
+ cd ../terraform
200
+ cp variables.example.tfvars my_variables.tfvars
201
+ # compléter project_id, region, etc.
202
+
203
+ terraform init
204
+ terraform apply --var-file=my_variables.tfvars
205
+ ```
206
+
207
+ Rôles requis pour le compte de service Terraform :
208
+ - Cloud SQL Admin
209
+ - Project IAM Admin
210
+ - Service Account Admin
211
+ - Service Usage Admin
212
+ - BigQuery Admin
213
+
214
+ #### Permissions PostgreSQL (si Cloud SQL)
215
+
216
+ ```sql
217
+ -- Base mocksql
218
+ GRANT USAGE, CREATE ON SCHEMA public TO "mocksql@${project_id}.iam";
219
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "mocksql@${project_id}.iam";
220
+ CREATE EXTENSION IF NOT EXISTS vector;
221
+
222
+ -- Base sqlmeshconf
223
+ GRANT USAGE, CREATE ON SCHEMA public TO "mocksql@${project_id}.iam";
224
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "mocksql@${project_id}.iam";
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Licence
230
+
231
+ Propriétaire — © 2025 Adel Skhiri. Contact : [skhiriadel92@gmail.com](mailto:skhiriadel92@gmail.com)
232
+
@@ -0,0 +1,187 @@
1
+ # MockSQL — Backend
2
+
3
+ **FastAPI + LangGraph + CLI**
4
+
5
+ Ce dossier contient le cœur de MockSQL : l'API REST, le graph LangGraph de génération de données, le CLI, et le package `mocksql-ui` (serveur web + assets React).
6
+
7
+ ---
8
+
9
+ ## Quickstart local
10
+
11
+ > Prérequis : Python ≥ 3.11, Poetry ≥ 1.8, un projet Google Cloud avec la facturation activée.
12
+
13
+ ### 1. Authentification Google Cloud
14
+
15
+ ```bash
16
+ gcloud auth application-default login
17
+ gcloud config set project <PROJECT_ID>
18
+ ```
19
+
20
+ ### 2. Variables d'environnement
21
+
22
+ ```bash
23
+ cp .env.example .env # puis compléter les valeurs
24
+ ```
25
+
26
+ Variables minimales requises :
27
+
28
+ ```dotenv
29
+ PROJECT_ID=<votre-projet-gcp>
30
+ GOOGLE_CLOUD_PROJECT=<votre-projet-gcp>
31
+ BQ_SCHEMA_BILLING_PROJECT=<votre-projet-gcp>
32
+ DEFAULT_MODEL_NAME=gemini-2.0-flash-lite
33
+ DUCKDB_PATH=data/mocksql.duckdb
34
+ SECRET_KEY=<générer avec : python -c "import secrets; print(secrets.token_hex(32))">
35
+ API_SECRET_KEY=<idem>
36
+ FRONT_URL=http://127.0.0.1:3000
37
+ ```
38
+
39
+ ### 3. Installation & lancement
40
+
41
+ ```bash
42
+ python -m venv .venv
43
+
44
+ # Linux / macOS
45
+ source .venv/bin/activate
46
+
47
+ # Windows
48
+ .\.venv\Scripts\activate
49
+
50
+ pip install poetry && poetry install
51
+
52
+ # Lancer le serveur
53
+ uvicorn server:app --port 8080 --reload
54
+ ```
55
+
56
+ L'API est accessible sur **http://localhost:8080**, le frontend sur **http://localhost:3000** (voir [../front/README.md](../front/README.md)).
57
+
58
+ ---
59
+
60
+ ## Commandes de développement
61
+
62
+ ```bash
63
+ make style # ruff check + ruff format --check + vulture (code mort)
64
+ make format # ruff format + ruff check --fix (auto-correction)
65
+ make test # pytest
66
+ make check # style + test
67
+ ```
68
+
69
+ Type checking :
70
+
71
+ ```bash
72
+ poetry run mypy build_query/ app/
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Packaging
78
+
79
+ MockSQL produit deux wheels indépendants :
80
+
81
+ | Wheel | Contenu |
82
+ |-------|---------|
83
+ | `mocksql-*.whl` | CLI + LangGraph core (sans UI) |
84
+ | `mocksql_ui-*.whl` | Serveur web + assets React bundlés |
85
+
86
+ ### Builder les wheels
87
+
88
+ ```bash
89
+ # CLI uniquement
90
+ make build-cli
91
+
92
+ # CLI + UI (build React inclus — Node.js 18+ requis)
93
+ make build-ui
94
+ ```
95
+
96
+ Les wheels sont générés dans `dist/`.
97
+
98
+ ### Lancer l'UI depuis les wheels
99
+
100
+ ```bash
101
+ pip install dist/mocksql-*.whl dist/mocksql_ui-*.whl
102
+ mocksql ui # http://localhost:8080/static/
103
+ mocksql ui --port 4000
104
+ mocksql ui --no-browser
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Déploiement
110
+
111
+ ### Variables d'environnement (production)
112
+
113
+ Voir `.env.example` pour la liste complète. En production (Cloud Run), les secrets sont gérés via **Google Secret Manager** ou les variables d'environnement du service.
114
+
115
+ ### Conteneurisation (Docker)
116
+
117
+ ```bash
118
+ docker build -t mocksql-backend .
119
+ docker run -d -p 8080:8080 \
120
+ -e PROJECT_ID=<votre-projet-gcp> \
121
+ -e GOOGLE_APPLICATION_CREDENTIALS=/keys/sa.json \
122
+ -v ~/keys:/keys \
123
+ mocksql-backend
124
+ ```
125
+
126
+ ### Google Cloud Run
127
+
128
+ #### 1. Build & push
129
+
130
+ ```bash
131
+ gcloud builds submit --tag gcr.io/${PROJECT_ID}/mocksql-backend .
132
+ ```
133
+
134
+ #### 2. Secrets
135
+
136
+ ```bash
137
+ gcloud secrets create mocksql-env --data-file .env
138
+ ```
139
+
140
+ #### 3. Déploiement
141
+
142
+ ```bash
143
+ gcloud run deploy mocksql-backend \
144
+ --image gcr.io/${PROJECT_ID}/mocksql-backend \
145
+ --region europe-west1 \
146
+ --platform managed \
147
+ --allow-unauthenticated \
148
+ --memory 1Gi \
149
+ --service-account mocksql-sa@${PROJECT_ID}.iam.gserviceaccount.com
150
+ ```
151
+
152
+ ### Infrastructure Terraform
153
+
154
+ ```bash
155
+ cd ../terraform
156
+ cp variables.example.tfvars my_variables.tfvars
157
+ # compléter project_id, region, etc.
158
+
159
+ terraform init
160
+ terraform apply --var-file=my_variables.tfvars
161
+ ```
162
+
163
+ Rôles requis pour le compte de service Terraform :
164
+ - Cloud SQL Admin
165
+ - Project IAM Admin
166
+ - Service Account Admin
167
+ - Service Usage Admin
168
+ - BigQuery Admin
169
+
170
+ #### Permissions PostgreSQL (si Cloud SQL)
171
+
172
+ ```sql
173
+ -- Base mocksql
174
+ GRANT USAGE, CREATE ON SCHEMA public TO "mocksql@${project_id}.iam";
175
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "mocksql@${project_id}.iam";
176
+ CREATE EXTENSION IF NOT EXISTS vector;
177
+
178
+ -- Base sqlmeshconf
179
+ GRANT USAGE, CREATE ON SCHEMA public TO "mocksql@${project_id}.iam";
180
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "mocksql@${project_id}.iam";
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Licence
186
+
187
+ Propriétaire — © 2025 Adel Skhiri. Contact : [skhiriadel92@gmail.com](mailto:skhiriadel92@gmail.com)
File without changes
File without changes
File without changes
@@ -0,0 +1,114 @@
1
+ import logging
2
+ from typing import List, Any, Optional
3
+
4
+ from fastapi import APIRouter, HTTPException
5
+ from pydantic import BaseModel
6
+
7
+ from storage.config import is_initialized
8
+ from storage.test_repository import get_test, update_test
9
+ from utils.saver import common_history_retriever
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ router = APIRouter()
14
+
15
+
16
+ class MessageRequest(BaseModel):
17
+ modelId: str
18
+
19
+
20
+ class PatchTestsRequest(BaseModel):
21
+ sessionId: str
22
+ tests: List[Any]
23
+
24
+
25
+ class PatchSqlRequest(BaseModel):
26
+ sessionId: str
27
+ sql: str
28
+ optimized_sql: str = ""
29
+ tests: Optional[List[Any]] = None
30
+ test_results: Optional[List[Any]] = None
31
+ restored_message_id: Optional[str] = None
32
+ last_error: Optional[str] = None
33
+
34
+
35
+ @router.post("/getMessages")
36
+ async def get_messages(body: MessageRequest):
37
+ if not is_initialized():
38
+ raise HTTPException(
39
+ status_code=400,
40
+ detail="Projet non initialisé. Lancez 'mocksql init' dans votre répertoire de travail pour commencer.",
41
+ )
42
+ try:
43
+ history = await common_history_retriever(body.modelId, filtered_types=[])
44
+ if history is None:
45
+ raise HTTPException(status_code=404, detail="Session not found")
46
+
47
+ test = get_test(body.modelId)
48
+ sql = test.get("sql") if test else None
49
+ optimized_sql = test.get("optimized_sql") if test else None
50
+ last_error = test.get("last_error") if test else ""
51
+ test_results = test.get("test_cases", []) if test else []
52
+ restored_message_id = test.get("restored_message_id") if test else None
53
+
54
+ # Fallback: extract from last results message in history
55
+ if not test_results:
56
+ import json
57
+
58
+ for msg in reversed(history):
59
+ if msg.additional_kwargs.get("type") == "results":
60
+ try:
61
+ test_results = json.loads(msg.content)
62
+ except Exception:
63
+ pass
64
+ break
65
+
66
+ return {
67
+ "messages": history,
68
+ "sql": sql,
69
+ "optimized_sql": optimized_sql,
70
+ "test_results": test_results,
71
+ "restored_message_id": restored_message_id,
72
+ "last_error": last_error or "",
73
+ "sql_history": [],
74
+ }
75
+ except HTTPException:
76
+ raise
77
+ except Exception as e:
78
+ logger.exception(
79
+ "Erreur lors du chargement des messages pour la session %s", body.modelId
80
+ )
81
+ raise HTTPException(status_code=500, detail=str(e))
82
+
83
+
84
+ @router.patch("/models/sql")
85
+ async def patch_model_sql(body: PatchSqlRequest):
86
+ try:
87
+ updates: dict = {
88
+ "sql": body.sql,
89
+ "optimized_sql": body.optimized_sql,
90
+ }
91
+ if body.tests is not None:
92
+ updates["test_cases"] = body.tests
93
+ if body.test_results is not None:
94
+ updates["test_cases"] = body.test_results
95
+ if body.restored_message_id is not None:
96
+ updates["restored_message_id"] = body.restored_message_id or None
97
+ if body.last_error is not None:
98
+ updates["last_error"] = body.last_error
99
+
100
+ update_test(body.sessionId, updates)
101
+ return {"ok": True}
102
+ except Exception as e:
103
+ print(e)
104
+ raise HTTPException(status_code=500, detail="Internal Server Error")
105
+
106
+
107
+ @router.patch("/models/tests")
108
+ async def patch_model_tests(body: PatchTestsRequest):
109
+ try:
110
+ update_test(body.sessionId, {"test_cases": body.tests})
111
+ return {"ok": True}
112
+ except Exception as e:
113
+ print(e)
114
+ raise HTTPException(status_code=500, detail="Internal Server Error")
@@ -0,0 +1,117 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ from fastapi.responses import JSONResponse
3
+ from pydantic import BaseModel
4
+
5
+ from storage.test_repository import (
6
+ list_models,
7
+ list_all_tests,
8
+ list_tests,
9
+ get_test,
10
+ create_test,
11
+ delete_test,
12
+ _test_path,
13
+ _read_json,
14
+ )
15
+
16
+ router = APIRouter()
17
+
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # SQL Models (fichiers physiques dans models_path)
21
+ # ---------------------------------------------------------------------------
22
+
23
+
24
+ @router.get("/models")
25
+ async def get_models():
26
+ """Liste les fichiers .sql disponibles dans models_path.
27
+
28
+ Each entry includes session_id / updated_at / test_name when a test already
29
+ exists for that model, so the frontend can redirect instead of regenerating.
30
+ Tested models are returned first (sorted by updated_at desc), then untested.
31
+ """
32
+ try:
33
+ sql_files = list_models()
34
+ tested = []
35
+ untested = []
36
+ for f in sql_files:
37
+ p = _test_path(f["name"])
38
+ if p.exists():
39
+ data = _read_json(p)
40
+ if data:
41
+ tested.append(
42
+ {
43
+ **f,
44
+ "session_id": data.get("test_id"),
45
+ "updated_at": data.get("updated_at"),
46
+ "test_name": data.get("test_name"),
47
+ }
48
+ )
49
+ continue
50
+ untested.append(f)
51
+ tested.sort(key=lambda x: x.get("updated_at") or "", reverse=True)
52
+ return tested + untested
53
+ except Exception as e:
54
+ raise HTTPException(status_code=500, detail=str(e))
55
+
56
+
57
+ @router.get("/tests/all")
58
+ async def get_all_tests():
59
+ """Liste toutes les sessions de test (tous models confondus), triées par date décroissante."""
60
+ try:
61
+ return list_all_tests()
62
+ except Exception as e:
63
+ raise HTTPException(status_code=500, detail=str(e))
64
+
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # Tests (fichiers dans .mocksql/tests/)
68
+ # ---------------------------------------------------------------------------
69
+
70
+
71
+ class CreateTestRequest(BaseModel):
72
+ model_name: str
73
+
74
+
75
+ @router.get("/tests")
76
+ async def get_tests(model_name: str):
77
+ """Liste tous les tests pour un model donné."""
78
+ try:
79
+ return list_tests(model_name)
80
+ except Exception as e:
81
+ raise HTTPException(status_code=500, detail=str(e))
82
+
83
+
84
+ @router.get("/test/{session_id}")
85
+ async def get_test_route(session_id: str, model_name: str = None):
86
+ try:
87
+ test = get_test(session_id, model_name)
88
+ if test is None:
89
+ raise HTTPException(status_code=404, detail="Test not found")
90
+ return test
91
+ except HTTPException:
92
+ raise
93
+ except Exception as e:
94
+ raise HTTPException(status_code=500, detail=str(e))
95
+
96
+
97
+ @router.post("/tests")
98
+ async def create_test_route(body: CreateTestRequest):
99
+ """Crée un nouveau test pour un model. Retourne le test_id (= session_id)."""
100
+ try:
101
+ test = create_test(body.model_name)
102
+ return JSONResponse(content=test, status_code=201)
103
+ except Exception as e:
104
+ raise HTTPException(status_code=500, detail=str(e))
105
+
106
+
107
+ @router.delete("/tests/{session_id}")
108
+ async def delete_test_route(session_id: str, model_name: str):
109
+ try:
110
+ ok = delete_test(session_id, model_name)
111
+ if not ok:
112
+ raise HTTPException(status_code=404, detail="Test not found")
113
+ return JSONResponse(status_code=204, content={"message": "Test deleted"})
114
+ except HTTPException:
115
+ raise
116
+ except Exception as e:
117
+ raise HTTPException(status_code=500, detail=str(e))