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.
- mocksql-0.1.0/PKG-INFO +232 -0
- mocksql-0.1.0/README.md +187 -0
- mocksql-0.1.0/app/__init__.py +0 -0
- mocksql-0.1.0/app/api/__init__.py +0 -0
- mocksql-0.1.0/app/api/endpoints/__init__.py +0 -0
- mocksql-0.1.0/app/api/endpoints/messages.py +114 -0
- mocksql-0.1.0/app/api/endpoints/models.py +117 -0
- mocksql-0.1.0/app/api/endpoints/projects.py +141 -0
- mocksql-0.1.0/app/api/endpoints/query.py +413 -0
- mocksql-0.1.0/app/api/endpoints/users.py +44 -0
- mocksql-0.1.0/app/exceptions/__init__.py +0 -0
- mocksql-0.1.0/app/exceptions/exceptions.py +4 -0
- mocksql-0.1.0/app/services/__init__.py +0 -0
- mocksql-0.1.0/app/services/query_service.py +2 -0
- mocksql-0.1.0/build_query/__init__.py +0 -0
- mocksql-0.1.0/build_query/constraint_simplifier.py +1066 -0
- mocksql-0.1.0/build_query/converstion_history.py +336 -0
- mocksql-0.1.0/build_query/examples_executor.py +870 -0
- mocksql-0.1.0/build_query/examples_generator.py +424 -0
- mocksql-0.1.0/build_query/other.py +62 -0
- mocksql-0.1.0/build_query/profile_checker.py +342 -0
- mocksql-0.1.0/build_query/profiler.py +2135 -0
- mocksql-0.1.0/build_query/prompt_tools.py +864 -0
- mocksql-0.1.0/build_query/query_chain.py +172 -0
- mocksql-0.1.0/build_query/query_executor.py +125 -0
- mocksql-0.1.0/build_query/routing.py +113 -0
- mocksql-0.1.0/build_query/schema_fetcher.py +138 -0
- mocksql-0.1.0/build_query/state.py +48 -0
- mocksql-0.1.0/build_query/test_evaluator.py +124 -0
- mocksql-0.1.0/build_query/validator.py +546 -0
- mocksql-0.1.0/cli/__init__.py +0 -0
- mocksql-0.1.0/cli/generate.py +388 -0
- mocksql-0.1.0/cli/main.py +321 -0
- mocksql-0.1.0/cli/test_runner.py +323 -0
- mocksql-0.1.0/common_vars.py +365 -0
- mocksql-0.1.0/fetch_secrets.py +34 -0
- mocksql-0.1.0/init/__init__.py +0 -0
- mocksql-0.1.0/init/add_column.py +153 -0
- mocksql-0.1.0/init/add_table.py +129 -0
- mocksql-0.1.0/init/create_user.py +72 -0
- mocksql-0.1.0/init/grant_access_to_db.py +31 -0
- mocksql-0.1.0/init/init_db.py +154 -0
- mocksql-0.1.0/models/__init__.py +0 -0
- mocksql-0.1.0/models/database.py +66 -0
- mocksql-0.1.0/models/db_pool.py +161 -0
- mocksql-0.1.0/models/env_variables.py +41 -0
- mocksql-0.1.0/models/message_service.py +286 -0
- mocksql-0.1.0/models/model.py +2 -0
- mocksql-0.1.0/models/model_service.py +2 -0
- mocksql-0.1.0/models/permissions.py +66 -0
- mocksql-0.1.0/models/schemas.py +122 -0
- mocksql-0.1.0/models/session_service.py +105 -0
- mocksql-0.1.0/models/user_service.py +99 -0
- mocksql-0.1.0/pyproject.toml +92 -0
- mocksql-0.1.0/server.py +95 -0
- mocksql-0.1.0/sql_functions/__init__.py +0 -0
- mocksql-0.1.0/sql_functions/functions.py +50 -0
- mocksql-0.1.0/sql_functions/helpers.py +276 -0
- mocksql-0.1.0/storage/__init__.py +0 -0
- mocksql-0.1.0/storage/config.py +47 -0
- mocksql-0.1.0/storage/test_repository.py +257 -0
- mocksql-0.1.0/utils/__init__.py +19 -0
- mocksql-0.1.0/utils/bigquery_test_helper.py +416 -0
- mocksql-0.1.0/utils/duckdb_test_helper.py +235 -0
- mocksql-0.1.0/utils/errors.py +376 -0
- mocksql-0.1.0/utils/examples.py +754 -0
- mocksql-0.1.0/utils/find_grains.py +822 -0
- mocksql-0.1.0/utils/insert_examples.py +366 -0
- mocksql-0.1.0/utils/llm_errors.py +25 -0
- mocksql-0.1.0/utils/logger.py +0 -0
- mocksql-0.1.0/utils/models.py +141 -0
- mocksql-0.1.0/utils/msg_types.py +25 -0
- mocksql-0.1.0/utils/postgres_db_utils.py +39 -0
- mocksql-0.1.0/utils/postgres_test_helper.py +178 -0
- mocksql-0.1.0/utils/prompt_utils.py +27 -0
- mocksql-0.1.0/utils/query_services.py +17 -0
- mocksql-0.1.0/utils/saver.py +270 -0
- mocksql-0.1.0/utils/schema_utils.py +125 -0
- 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
|
+
|
mocksql-0.1.0/README.md
ADDED
|
@@ -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))
|