mdb-engine 0.2.4__tar.gz → 0.3.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.
- {mdb_engine-0.2.4/mdb_engine.egg-info → mdb_engine-0.3.0}/PKG-INFO +127 -23
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/README.md +126 -22
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/__init__.py +2 -1
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/engine.py +417 -5
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/manifest.py +93 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0/mdb_engine.egg-info}/PKG-INFO +127 -23
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/pyproject.toml +1 -1
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/setup.py +1 -1
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/LICENSE +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/MANIFEST.in +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/ARCHITECTURE.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/audit.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/base.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/casbin_factory.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/casbin_models.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/config_defaults.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/config_helpers.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/cookie_utils.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/csrf.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/decorators.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/dependencies.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/helpers.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/integration.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/jwt.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/middleware.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/oso_factory.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/provider.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/rate_limiter.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/restrictions.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/session_manager.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/shared_middleware.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/shared_users.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/token_lifecycle.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/token_store.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/users.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/auth/utils.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/commands/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/commands/generate.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/commands/migrate.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/commands/show.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/commands/validate.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/main.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/cli/utils.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/config.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/constants.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/app_registration.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/app_secrets.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/connection.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/encryption.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/index_management.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/ray_integration.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/seeding.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/service_initialization.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/core/types.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/abstraction.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/connection.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/query_validator.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/resource_limiter.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/database/scoped_wrapper.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/dependencies.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/di/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/di/container.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/di/providers.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/di/scopes.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/embeddings/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/embeddings/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/embeddings/dependencies.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/embeddings/service.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/exceptions.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/indexes/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/indexes/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/indexes/helpers.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/indexes/manager.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/memory/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/memory/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/memory/service.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/observability/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/observability/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/observability/health.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/observability/logging.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/observability/metrics.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/repositories/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/repositories/base.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/repositories/mongo.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/repositories/unit_of_work.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/routing/README.md +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/routing/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/routing/websockets.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/utils/__init__.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine/utils/mongo.py +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine.egg-info/SOURCES.txt +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine.egg-info/dependency_links.txt +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine.egg-info/entry_points.txt +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine.egg-info/requires.txt +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/mdb_engine.egg-info/top_level.txt +0 -0
- {mdb_engine-0.2.4 → mdb_engine-0.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdb-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: MongoDB Engine
|
|
5
5
|
Home-page: https://github.com/ranfysvalle02/mdb-engine
|
|
6
6
|
Author: Fabian Valle
|
|
@@ -106,58 +106,162 @@ pip install mdb-engine
|
|
|
106
106
|
|
|
107
107
|
---
|
|
108
108
|
|
|
109
|
-
## 30-Second Quick Start
|
|
109
|
+
## 30-Second Quick Start: Build a Todo List API
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
Let's build a complete CRUD todo list app in 3 steps!
|
|
112
|
+
|
|
113
|
+
### Step 1: Create `manifest.json`
|
|
112
114
|
|
|
113
115
|
```json
|
|
114
116
|
{
|
|
115
117
|
"schema_version": "2.0",
|
|
116
|
-
"slug": "
|
|
117
|
-
"name": "
|
|
118
|
+
"slug": "todo_app",
|
|
119
|
+
"name": "Todo List App",
|
|
118
120
|
"managed_indexes": {
|
|
119
|
-
"
|
|
121
|
+
"todos": [
|
|
120
122
|
{
|
|
121
123
|
"type": "regular",
|
|
122
|
-
"keys": {"
|
|
123
|
-
"name": "
|
|
124
|
+
"keys": {"completed": 1, "created_at": -1},
|
|
125
|
+
"name": "completed_sort"
|
|
124
126
|
}
|
|
125
127
|
]
|
|
126
128
|
}
|
|
127
129
|
}
|
|
128
130
|
```
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
### Step 2: Create `app.py` with Full CRUD
|
|
131
133
|
|
|
132
134
|
```python
|
|
135
|
+
from datetime import datetime
|
|
133
136
|
from pathlib import Path
|
|
134
|
-
from
|
|
137
|
+
from typing import Optional
|
|
138
|
+
|
|
139
|
+
from bson import ObjectId
|
|
140
|
+
from fastapi import Depends, HTTPException
|
|
141
|
+
from pydantic import BaseModel
|
|
142
|
+
|
|
135
143
|
from mdb_engine import MongoDBEngine
|
|
136
144
|
from mdb_engine.dependencies import get_scoped_db
|
|
137
145
|
|
|
138
|
-
# Initialize
|
|
146
|
+
# Initialize engine
|
|
139
147
|
engine = MongoDBEngine(
|
|
140
148
|
mongo_uri="mongodb://localhost:27017",
|
|
141
149
|
db_name="my_database"
|
|
142
150
|
)
|
|
143
151
|
|
|
144
|
-
# Create app - manifest.json
|
|
145
|
-
app = engine.create_app(
|
|
152
|
+
# Create app - manifest.json loaded automatically!
|
|
153
|
+
app = engine.create_app(
|
|
154
|
+
slug="todo_app",
|
|
155
|
+
manifest=Path("manifest.json")
|
|
156
|
+
)
|
|
146
157
|
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
# Pydantic models
|
|
159
|
+
class TodoCreate(BaseModel):
|
|
160
|
+
title: str
|
|
161
|
+
description: Optional[str] = None
|
|
162
|
+
|
|
163
|
+
class TodoUpdate(BaseModel):
|
|
164
|
+
title: Optional[str] = None
|
|
165
|
+
description: Optional[str] = None
|
|
166
|
+
completed: Optional[bool] = None
|
|
167
|
+
|
|
168
|
+
# CREATE - Add a new todo
|
|
169
|
+
@app.post("/todos")
|
|
170
|
+
async def create_todo(todo: TodoCreate, db=Depends(get_scoped_db)):
|
|
171
|
+
doc = {
|
|
172
|
+
**todo.dict(),
|
|
173
|
+
"completed": False,
|
|
174
|
+
"created_at": datetime.utcnow()
|
|
175
|
+
}
|
|
176
|
+
result = await db.todos.insert_one(doc)
|
|
177
|
+
return {"id": str(result.inserted_id), "message": "Todo created"}
|
|
178
|
+
|
|
179
|
+
# READ - List all todos
|
|
180
|
+
@app.get("/todos")
|
|
181
|
+
async def list_todos(completed: Optional[bool] = None, db=Depends(get_scoped_db)):
|
|
182
|
+
query = {}
|
|
183
|
+
if completed is not None:
|
|
184
|
+
query["completed"] = completed
|
|
185
|
+
|
|
186
|
+
todos = await db.todos.find(query).sort("created_at", -1).to_list(length=100)
|
|
187
|
+
for todo in todos:
|
|
188
|
+
todo["_id"] = str(todo["_id"])
|
|
189
|
+
return {"todos": todos, "count": len(todos)}
|
|
190
|
+
|
|
191
|
+
# READ - Get single todo
|
|
192
|
+
@app.get("/todos/{todo_id}")
|
|
193
|
+
async def get_todo(todo_id: str, db=Depends(get_scoped_db)):
|
|
194
|
+
todo = await db.todos.find_one({"_id": ObjectId(todo_id)})
|
|
195
|
+
if not todo:
|
|
196
|
+
raise HTTPException(status_code=404, detail="Todo not found")
|
|
197
|
+
todo["_id"] = str(todo["_id"])
|
|
198
|
+
return todo
|
|
199
|
+
|
|
200
|
+
# UPDATE - Update a todo
|
|
201
|
+
@app.put("/todos/{todo_id}")
|
|
202
|
+
async def update_todo(todo_id: str, todo: TodoUpdate, db=Depends(get_scoped_db)):
|
|
203
|
+
updates = {k: v for k, v in todo.dict(exclude_unset=True).items() if v is not None}
|
|
204
|
+
if not updates:
|
|
205
|
+
raise HTTPException(status_code=400, detail="No fields to update")
|
|
206
|
+
|
|
207
|
+
updates["updated_at"] = datetime.utcnow()
|
|
208
|
+
result = await db.todos.update_one(
|
|
209
|
+
{"_id": ObjectId(todo_id)},
|
|
210
|
+
{"$set": updates}
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if result.matched_count == 0:
|
|
214
|
+
raise HTTPException(status_code=404, detail="Todo not found")
|
|
215
|
+
return {"message": "Todo updated"}
|
|
216
|
+
|
|
217
|
+
# DELETE - Delete a todo
|
|
218
|
+
@app.delete("/todos/{todo_id}")
|
|
219
|
+
async def delete_todo(todo_id: str, db=Depends(get_scoped_db)):
|
|
220
|
+
result = await db.todos.delete_one({"_id": ObjectId(todo_id)})
|
|
221
|
+
if result.deleted_count == 0:
|
|
222
|
+
raise HTTPException(status_code=404, detail="Todo not found")
|
|
223
|
+
return {"message": "Todo deleted"}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Step 3: Run It!
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Start MongoDB (if not running)
|
|
230
|
+
mongod
|
|
231
|
+
|
|
232
|
+
# Install dependencies
|
|
233
|
+
pip install mdb-engine fastapi uvicorn
|
|
234
|
+
|
|
235
|
+
# Run the app
|
|
236
|
+
uvicorn app:app --reload
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Test your API:**
|
|
240
|
+
```bash
|
|
241
|
+
# Create a todo
|
|
242
|
+
curl -X POST http://localhost:8000/todos \
|
|
243
|
+
-H "Content-Type: application/json" \
|
|
244
|
+
-d '{"title": "Buy groceries", "description": "Milk and eggs"}'
|
|
245
|
+
|
|
246
|
+
# List todos
|
|
247
|
+
curl http://localhost:8000/todos
|
|
248
|
+
|
|
249
|
+
# Update a todo (replace {id} with actual ID)
|
|
250
|
+
curl -X PUT http://localhost:8000/todos/{id} \
|
|
251
|
+
-H "Content-Type: application/json" \
|
|
252
|
+
-d '{"completed": true}'
|
|
253
|
+
|
|
254
|
+
# Delete a todo
|
|
255
|
+
curl -X DELETE http://localhost:8000/todos/{id}
|
|
152
256
|
```
|
|
153
257
|
|
|
154
258
|
**What just happened?**
|
|
155
|
-
- ✅
|
|
156
|
-
- ✅ Indexes created
|
|
157
|
-
- ✅
|
|
158
|
-
- ✅
|
|
259
|
+
- ✅ **Automatic scoping**: All queries filtered by `app_id` — your data is isolated
|
|
260
|
+
- ✅ **Indexes created**: The `completed_sort` index was created automatically
|
|
261
|
+
- ✅ **Lifecycle managed**: Startup/shutdown handled automatically
|
|
262
|
+
- ✅ **Zero boilerplate**: No connection setup, no index scripts, no auth handlers
|
|
159
263
|
|
|
160
|
-
That's it
|
|
264
|
+
**That's it!** You now have a fully functional, production-ready todo API with automatic data sandboxing, index management, and lifecycle handling.
|
|
161
265
|
|
|
162
266
|
---
|
|
163
267
|
|
|
@@ -41,58 +41,162 @@ pip install mdb-engine
|
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
## 30-Second Quick Start
|
|
44
|
+
## 30-Second Quick Start: Build a Todo List API
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Let's build a complete CRUD todo list app in 3 steps!
|
|
47
|
+
|
|
48
|
+
### Step 1: Create `manifest.json`
|
|
47
49
|
|
|
48
50
|
```json
|
|
49
51
|
{
|
|
50
52
|
"schema_version": "2.0",
|
|
51
|
-
"slug": "
|
|
52
|
-
"name": "
|
|
53
|
+
"slug": "todo_app",
|
|
54
|
+
"name": "Todo List App",
|
|
53
55
|
"managed_indexes": {
|
|
54
|
-
"
|
|
56
|
+
"todos": [
|
|
55
57
|
{
|
|
56
58
|
"type": "regular",
|
|
57
|
-
"keys": {"
|
|
58
|
-
"name": "
|
|
59
|
+
"keys": {"completed": 1, "created_at": -1},
|
|
60
|
+
"name": "completed_sort"
|
|
59
61
|
}
|
|
60
62
|
]
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
### Step 2: Create `app.py` with Full CRUD
|
|
66
68
|
|
|
67
69
|
```python
|
|
70
|
+
from datetime import datetime
|
|
68
71
|
from pathlib import Path
|
|
69
|
-
from
|
|
72
|
+
from typing import Optional
|
|
73
|
+
|
|
74
|
+
from bson import ObjectId
|
|
75
|
+
from fastapi import Depends, HTTPException
|
|
76
|
+
from pydantic import BaseModel
|
|
77
|
+
|
|
70
78
|
from mdb_engine import MongoDBEngine
|
|
71
79
|
from mdb_engine.dependencies import get_scoped_db
|
|
72
80
|
|
|
73
|
-
# Initialize
|
|
81
|
+
# Initialize engine
|
|
74
82
|
engine = MongoDBEngine(
|
|
75
83
|
mongo_uri="mongodb://localhost:27017",
|
|
76
84
|
db_name="my_database"
|
|
77
85
|
)
|
|
78
86
|
|
|
79
|
-
# Create app - manifest.json
|
|
80
|
-
app = engine.create_app(
|
|
87
|
+
# Create app - manifest.json loaded automatically!
|
|
88
|
+
app = engine.create_app(
|
|
89
|
+
slug="todo_app",
|
|
90
|
+
manifest=Path("manifest.json")
|
|
91
|
+
)
|
|
81
92
|
|
|
82
|
-
#
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
# Pydantic models
|
|
94
|
+
class TodoCreate(BaseModel):
|
|
95
|
+
title: str
|
|
96
|
+
description: Optional[str] = None
|
|
97
|
+
|
|
98
|
+
class TodoUpdate(BaseModel):
|
|
99
|
+
title: Optional[str] = None
|
|
100
|
+
description: Optional[str] = None
|
|
101
|
+
completed: Optional[bool] = None
|
|
102
|
+
|
|
103
|
+
# CREATE - Add a new todo
|
|
104
|
+
@app.post("/todos")
|
|
105
|
+
async def create_todo(todo: TodoCreate, db=Depends(get_scoped_db)):
|
|
106
|
+
doc = {
|
|
107
|
+
**todo.dict(),
|
|
108
|
+
"completed": False,
|
|
109
|
+
"created_at": datetime.utcnow()
|
|
110
|
+
}
|
|
111
|
+
result = await db.todos.insert_one(doc)
|
|
112
|
+
return {"id": str(result.inserted_id), "message": "Todo created"}
|
|
113
|
+
|
|
114
|
+
# READ - List all todos
|
|
115
|
+
@app.get("/todos")
|
|
116
|
+
async def list_todos(completed: Optional[bool] = None, db=Depends(get_scoped_db)):
|
|
117
|
+
query = {}
|
|
118
|
+
if completed is not None:
|
|
119
|
+
query["completed"] = completed
|
|
120
|
+
|
|
121
|
+
todos = await db.todos.find(query).sort("created_at", -1).to_list(length=100)
|
|
122
|
+
for todo in todos:
|
|
123
|
+
todo["_id"] = str(todo["_id"])
|
|
124
|
+
return {"todos": todos, "count": len(todos)}
|
|
125
|
+
|
|
126
|
+
# READ - Get single todo
|
|
127
|
+
@app.get("/todos/{todo_id}")
|
|
128
|
+
async def get_todo(todo_id: str, db=Depends(get_scoped_db)):
|
|
129
|
+
todo = await db.todos.find_one({"_id": ObjectId(todo_id)})
|
|
130
|
+
if not todo:
|
|
131
|
+
raise HTTPException(status_code=404, detail="Todo not found")
|
|
132
|
+
todo["_id"] = str(todo["_id"])
|
|
133
|
+
return todo
|
|
134
|
+
|
|
135
|
+
# UPDATE - Update a todo
|
|
136
|
+
@app.put("/todos/{todo_id}")
|
|
137
|
+
async def update_todo(todo_id: str, todo: TodoUpdate, db=Depends(get_scoped_db)):
|
|
138
|
+
updates = {k: v for k, v in todo.dict(exclude_unset=True).items() if v is not None}
|
|
139
|
+
if not updates:
|
|
140
|
+
raise HTTPException(status_code=400, detail="No fields to update")
|
|
141
|
+
|
|
142
|
+
updates["updated_at"] = datetime.utcnow()
|
|
143
|
+
result = await db.todos.update_one(
|
|
144
|
+
{"_id": ObjectId(todo_id)},
|
|
145
|
+
{"$set": updates}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if result.matched_count == 0:
|
|
149
|
+
raise HTTPException(status_code=404, detail="Todo not found")
|
|
150
|
+
return {"message": "Todo updated"}
|
|
151
|
+
|
|
152
|
+
# DELETE - Delete a todo
|
|
153
|
+
@app.delete("/todos/{todo_id}")
|
|
154
|
+
async def delete_todo(todo_id: str, db=Depends(get_scoped_db)):
|
|
155
|
+
result = await db.todos.delete_one({"_id": ObjectId(todo_id)})
|
|
156
|
+
if result.deleted_count == 0:
|
|
157
|
+
raise HTTPException(status_code=404, detail="Todo not found")
|
|
158
|
+
return {"message": "Todo deleted"}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Step 3: Run It!
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Start MongoDB (if not running)
|
|
165
|
+
mongod
|
|
166
|
+
|
|
167
|
+
# Install dependencies
|
|
168
|
+
pip install mdb-engine fastapi uvicorn
|
|
169
|
+
|
|
170
|
+
# Run the app
|
|
171
|
+
uvicorn app:app --reload
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Test your API:**
|
|
175
|
+
```bash
|
|
176
|
+
# Create a todo
|
|
177
|
+
curl -X POST http://localhost:8000/todos \
|
|
178
|
+
-H "Content-Type: application/json" \
|
|
179
|
+
-d '{"title": "Buy groceries", "description": "Milk and eggs"}'
|
|
180
|
+
|
|
181
|
+
# List todos
|
|
182
|
+
curl http://localhost:8000/todos
|
|
183
|
+
|
|
184
|
+
# Update a todo (replace {id} with actual ID)
|
|
185
|
+
curl -X PUT http://localhost:8000/todos/{id} \
|
|
186
|
+
-H "Content-Type: application/json" \
|
|
187
|
+
-d '{"completed": true}'
|
|
188
|
+
|
|
189
|
+
# Delete a todo
|
|
190
|
+
curl -X DELETE http://localhost:8000/todos/{id}
|
|
87
191
|
```
|
|
88
192
|
|
|
89
193
|
**What just happened?**
|
|
90
|
-
- ✅
|
|
91
|
-
- ✅ Indexes created
|
|
92
|
-
- ✅
|
|
93
|
-
- ✅
|
|
194
|
+
- ✅ **Automatic scoping**: All queries filtered by `app_id` — your data is isolated
|
|
195
|
+
- ✅ **Indexes created**: The `completed_sort` index was created automatically
|
|
196
|
+
- ✅ **Lifecycle managed**: Startup/shutdown handled automatically
|
|
197
|
+
- ✅ **Zero boilerplate**: No connection setup, no index scripts, no auth handlers
|
|
94
198
|
|
|
95
|
-
That's it
|
|
199
|
+
**That's it!** You now have a fully functional, production-ready todo API with automatic data sandboxing, index management, and lifecycle handling.
|
|
96
200
|
|
|
97
201
|
---
|
|
98
202
|
|
|
@@ -81,7 +81,8 @@ from .repositories import Entity, MongoRepository, Repository, UnitOfWork
|
|
|
81
81
|
# Utilities
|
|
82
82
|
from .utils import clean_mongo_doc, clean_mongo_docs
|
|
83
83
|
|
|
84
|
-
__version__ = "0.
|
|
84
|
+
__version__ = "0.3.0" # Minor version bump: Multi-app mounting, SSO improvements,
|
|
85
|
+
# and exception handling fixes
|
|
85
86
|
|
|
86
87
|
__all__ = [
|
|
87
88
|
# Core Engine
|