vention-storage 0.5.1__tar.gz → 0.5.2__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.
@@ -0,0 +1,318 @@
1
+ Metadata-Version: 2.1
2
+ Name: vention-storage
3
+ Version: 0.5.2
4
+ Summary: A framework for storing and managing component and application data for machine apps.
5
+ License: Proprietary
6
+ Author: VentionCo
7
+ Requires-Python: >=3.10,<3.11
8
+ Classifier: License :: Other/Proprietary License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Requires-Dist: fastapi (==0.121.1)
12
+ Requires-Dist: python-multipart (>=0.0.20,<0.0.21)
13
+ Requires-Dist: sqlmodel (==0.0.27)
14
+ Requires-Dist: uvicorn (>=0.35.0,<0.36.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Vention Storage
18
+
19
+ A framework for storing and managing component and application data with persistence, validation, and audit trails for machine applications.
20
+
21
+ ## Table of Contents
22
+
23
+ - [✨ Features](#-features)
24
+ - [🧠 Concepts & Overview](#-concepts--overview)
25
+ - [⚙️ Installation & Setup](#️-installation--setup)
26
+ - [🚀 Quickstart Tutorial](#-quickstart-tutorial)
27
+ - [🛠 How-to Guides](#-how-to-guides)
28
+ - [📖 API Reference](#-api-reference)
29
+ - [🔍 Troubleshooting & FAQ](#-troubleshooting--faq)
30
+
31
+ ## ✨ Features
32
+
33
+ - Persistent storage with SQLite
34
+ - Automatic audit trails (who, when, what changed)
35
+ - Strong typing & validation via SQLModel
36
+ - Lifecycle hooks before/after insert, update, delete
37
+ - Soft delete with `deleted_at` fields
38
+ - REST API generation with Create, Read, Update, Delete + audit
39
+ - Health & monitoring endpoints (audit log, schema diagram)
40
+ - Batch operations for insert/delete
41
+ - Session management with smart reuse & transactions
42
+ - Bootstrap system for one-command setup
43
+ - CSV export/import for backups and migration
44
+ - Database backup/restore with integrity checking
45
+
46
+ ## 🧠 Concepts & Overview
47
+
48
+ Vention Storage is a component-based persistence layer for machine apps:
49
+
50
+ - **Database** → SQLite database with managed sessions and transactions
51
+ - **ModelAccessor** → Strongly-typed Create, Read, Update, Delete interface for your SQLModel classes
52
+ - **Hooks** → Functions that run before/after Create, Read, Update, Delete operations
53
+ - **AuditLog** → Automatically records all data mutations
54
+ - **Routers** → Auto-generated FastAPI endpoints for Create, Read, Update, Delete + database management
55
+
56
+ ## ⚙️ Installation & Setup
57
+
58
+ ```bash
59
+ pip install vention-storage
60
+ ```
61
+
62
+ **Optional dependencies:**
63
+ - sqlalchemy-schemadisplay and Graphviz → enable database schema visualization
64
+
65
+ MacOS:
66
+ ```bash
67
+ brew install graphviz
68
+ pip install sqlalchemy-schemadisplay
69
+ ```
70
+
71
+ Linux (Debian/Ubuntu)
72
+ ```bash
73
+ sudo apt-get install graphviz
74
+ pip install sqlalchemy-schemadisplay
75
+ ```
76
+
77
+ ## 🚀 Quickstart Tutorial
78
+
79
+ Define a model, bootstrap storage, and get full Create, Read, Update, Delete endpoints in minutes:
80
+
81
+ ```python
82
+ from datetime import datetime
83
+ from typing import Optional
84
+ from sqlmodel import Field, SQLModel
85
+ from fastapi import FastAPI
86
+ from storage.bootstrap import bootstrap
87
+ from storage.accessor import ModelAccessor
88
+
89
+ class User(SQLModel, table=True):
90
+ id: Optional[int] = Field(default=None, primary_key=True)
91
+ name: str
92
+ email: str
93
+ deleted_at: Optional[datetime] = Field(default=None, index=True)
94
+
95
+ app = FastAPI()
96
+ user_accessor = ModelAccessor(User, "users")
97
+
98
+ bootstrap(
99
+ app,
100
+ accessors=[user_accessor],
101
+ database_url="sqlite:///./my_app.db",
102
+ create_tables=True
103
+ )
104
+ ```
105
+
106
+ ➡️ You now have Create, Read, Update, Delete, audit, backup, and CSV endpoints at `/users` and `/db`.
107
+
108
+ ## 🛠 How-to Guides
109
+
110
+ ### Bootstrap Multiple Models
111
+
112
+ ```python
113
+ # user_accessor was created earlier in the Quickstart example
114
+ # Reuse it here to bootstrap multiple models at once
115
+
116
+ product_accessor = ModelAccessor(Product, "products")
117
+
118
+ bootstrap(
119
+ app,
120
+ accessors=[user_accessor, product_accessor],
121
+ database_url="sqlite:///./my_app.db",
122
+ create_tables=True,
123
+ max_records_per_model=100,
124
+ enable_db_router=True
125
+ )
126
+ ```
127
+
128
+ ### Export to CSV
129
+
130
+ ```python
131
+ response = requests.get("http://localhost:8000/db/export.zip")
132
+ with open("backup.zip", "wb") as f:
133
+ f.write(response.content)
134
+ ```
135
+
136
+ ### Backup & Restore
137
+
138
+ ```python
139
+ # Backup
140
+ r = requests.get("http://localhost:8000/db/backup.sqlite")
141
+ with open("backup.sqlite", "wb") as f: f.write(r.content)
142
+
143
+ # Restore
144
+ with open("backup.sqlite", "rb") as f:
145
+ files = {"file": ("backup.sqlite", f, "application/x-sqlite3")}
146
+ requests.post("http://localhost:8000/db/restore", files=files)
147
+ ```
148
+
149
+ ### Use Lifecycle Hooks
150
+
151
+ ```python
152
+ @user_accessor.before_insert()
153
+ def validate_email(session, instance):
154
+ if "@" not in instance.email:
155
+ raise ValueError("Invalid email")
156
+
157
+ @user_accessor.after_insert()
158
+ def log_creation(session, instance):
159
+ print(f"User created: {instance.name}")
160
+ ```
161
+
162
+ ### Query Audit Logs
163
+
164
+ ```python
165
+ from storage.auditor import AuditLog
166
+ from sqlmodel import select
167
+
168
+ with database.transaction() as session:
169
+ logs = session.exec(select(AuditLog).where(AuditLog.component == "users")).all()
170
+ ```
171
+
172
+ ### Using the model accessors
173
+
174
+ ```python
175
+ # Create
176
+ user = user_accessor.insert(User(name="Alice", email="alice@example.com"), actor="admin")
177
+
178
+ # Read
179
+ user = user_accessor.get(user.id)
180
+
181
+ # Update
182
+ user.name = "Alice Smith"
183
+ user_accessor.save(user, actor="admin")
184
+
185
+ # Delete
186
+ user_accessor.delete(user.id, actor="admin")
187
+
188
+ # Restore (for soft-deleted models)
189
+ user_accessor.restore(user.id, actor="admin")
190
+ ```
191
+
192
+
193
+ ### Using the REST API
194
+
195
+ Once bootstrapped, each `ModelAccessor` automatically exposes full CRUD endpoints.
196
+
197
+ Example: interacting with the `/users` API.
198
+
199
+ ```typescript
200
+ import axios from "axios";
201
+
202
+ const api = axios.create({
203
+ baseURL: "http://localhost:8000", // your backend url
204
+ headers: { "X-User": "operator" }, // used in audit logs
205
+ });
206
+
207
+ // Create
208
+ export async function createUser(name: string, email: string) {
209
+ const res = await api.post("/users/", { name, email });
210
+ return res.data;
211
+ }
212
+
213
+ // Read
214
+ export async function getUser(id: number) {
215
+ const res = await api.get(`/users/${id}`);
216
+ return res.data;
217
+ }
218
+
219
+ // Update
220
+ export async function updateUser(id: number, name: string) {
221
+ const res = await api.put(`/users/${id}`, { name });
222
+ return res.data;
223
+ }
224
+
225
+ // Delete (soft delete if model supports deleted_at)
226
+ export async function deleteUser(id: number) {
227
+ await api.delete(`/users/${id}`);
228
+ }
229
+
230
+ // Restore
231
+ export async function restoreUser(id: number) {
232
+ await api.post(`/users/${id}/restore`);
233
+ }
234
+
235
+ // List
236
+ export async function listUsers() {
237
+ const res = await api.get("/users/");
238
+ return res.data;
239
+ }
240
+ ```
241
+
242
+
243
+ ## 📖 API Reference
244
+
245
+ ### bootstrap
246
+
247
+ ```python
248
+ def bootstrap(
249
+ app: FastAPI,
250
+ *,
251
+ accessors: Iterable[ModelAccessor[Any]],
252
+ database_url: Optional[str] = None,
253
+ create_tables: bool = True,
254
+ max_records_per_model: Optional[int] = 5,
255
+ enable_db_router: bool = True,
256
+ ) -> None
257
+ ```
258
+
259
+ ### ModelAccessor
260
+
261
+ ```python
262
+ ModelAccessor(model: Type[ModelType], component_name: str)
263
+ ```
264
+
265
+ **Read**
266
+ - `get(id, include_deleted=False) -> Optional[ModelType]`
267
+ - `all(include_deleted=False) -> List[ModelType]`
268
+
269
+ **Write**
270
+ - `insert(obj, actor="internal") -> ModelType`
271
+ - `save(obj, actor="internal") -> ModelType`
272
+ - `delete(id, actor="internal") -> bool`
273
+ - `restore(id, actor="internal") -> bool`
274
+
275
+ **Batch**
276
+ - `insert_many(objs, actor="internal") -> List[ModelType]`
277
+ - `delete_many(ids, actor="internal") -> int`
278
+
279
+ **Hooks**
280
+ - `@accessor.before_insert()`
281
+ - `@accessor.after_insert()`
282
+ - `@accessor.before_update()`
283
+ - `@accessor.after_update()`
284
+ - `@accessor.before_delete()`
285
+ - `@accessor.after_delete()`
286
+
287
+ ### Routers
288
+
289
+ - `build_crud_router(accessor, max_records=100) -> APIRouter`
290
+ - `build_db_router(audit_default_limit=100, audit_max_limit=1000) -> APIRouter`
291
+
292
+ ### Database Helpers
293
+
294
+ - `database.set_database_url(url: str) -> None`
295
+ - `database.get_engine() -> Engine`
296
+ - `database.transaction() -> Iterator[Session]`
297
+ - `database.use_session(session: Optional[Session] = None) -> Iterator[Session]`
298
+
299
+ ### AuditLog model
300
+
301
+ ```python
302
+ class AuditLog(SQLModel, table=True):
303
+ id: int
304
+ timestamp: datetime
305
+ component: str
306
+ record_id: int
307
+ operation: str
308
+ actor: str
309
+ before: Optional[Dict[str, Any]]
310
+ after: Optional[Dict[str, Any]]
311
+ ```
312
+
313
+ ## 🔍 Troubleshooting & FAQ
314
+
315
+ - **Diagram endpoint fails** → Ensure Graphviz + sqlalchemy-schemadisplay are installed.
316
+ - **No audit actor shown** → Provide X-User header in API requests.
317
+ - **Soft delete not working** → Your model must have a `deleted_at` field.
318
+ - **Restore fails** → Ensure `integrity_check=True` passes when restoring backups.
@@ -0,0 +1,302 @@
1
+ # Vention Storage
2
+
3
+ A framework for storing and managing component and application data with persistence, validation, and audit trails for machine applications.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [🧠 Concepts & Overview](#-concepts--overview)
9
+ - [⚙️ Installation & Setup](#️-installation--setup)
10
+ - [🚀 Quickstart Tutorial](#-quickstart-tutorial)
11
+ - [🛠 How-to Guides](#-how-to-guides)
12
+ - [📖 API Reference](#-api-reference)
13
+ - [🔍 Troubleshooting & FAQ](#-troubleshooting--faq)
14
+
15
+ ## ✨ Features
16
+
17
+ - Persistent storage with SQLite
18
+ - Automatic audit trails (who, when, what changed)
19
+ - Strong typing & validation via SQLModel
20
+ - Lifecycle hooks before/after insert, update, delete
21
+ - Soft delete with `deleted_at` fields
22
+ - REST API generation with Create, Read, Update, Delete + audit
23
+ - Health & monitoring endpoints (audit log, schema diagram)
24
+ - Batch operations for insert/delete
25
+ - Session management with smart reuse & transactions
26
+ - Bootstrap system for one-command setup
27
+ - CSV export/import for backups and migration
28
+ - Database backup/restore with integrity checking
29
+
30
+ ## 🧠 Concepts & Overview
31
+
32
+ Vention Storage is a component-based persistence layer for machine apps:
33
+
34
+ - **Database** → SQLite database with managed sessions and transactions
35
+ - **ModelAccessor** → Strongly-typed Create, Read, Update, Delete interface for your SQLModel classes
36
+ - **Hooks** → Functions that run before/after Create, Read, Update, Delete operations
37
+ - **AuditLog** → Automatically records all data mutations
38
+ - **Routers** → Auto-generated FastAPI endpoints for Create, Read, Update, Delete + database management
39
+
40
+ ## ⚙️ Installation & Setup
41
+
42
+ ```bash
43
+ pip install vention-storage
44
+ ```
45
+
46
+ **Optional dependencies:**
47
+ - sqlalchemy-schemadisplay and Graphviz → enable database schema visualization
48
+
49
+ MacOS:
50
+ ```bash
51
+ brew install graphviz
52
+ pip install sqlalchemy-schemadisplay
53
+ ```
54
+
55
+ Linux (Debian/Ubuntu)
56
+ ```bash
57
+ sudo apt-get install graphviz
58
+ pip install sqlalchemy-schemadisplay
59
+ ```
60
+
61
+ ## 🚀 Quickstart Tutorial
62
+
63
+ Define a model, bootstrap storage, and get full Create, Read, Update, Delete endpoints in minutes:
64
+
65
+ ```python
66
+ from datetime import datetime
67
+ from typing import Optional
68
+ from sqlmodel import Field, SQLModel
69
+ from fastapi import FastAPI
70
+ from storage.bootstrap import bootstrap
71
+ from storage.accessor import ModelAccessor
72
+
73
+ class User(SQLModel, table=True):
74
+ id: Optional[int] = Field(default=None, primary_key=True)
75
+ name: str
76
+ email: str
77
+ deleted_at: Optional[datetime] = Field(default=None, index=True)
78
+
79
+ app = FastAPI()
80
+ user_accessor = ModelAccessor(User, "users")
81
+
82
+ bootstrap(
83
+ app,
84
+ accessors=[user_accessor],
85
+ database_url="sqlite:///./my_app.db",
86
+ create_tables=True
87
+ )
88
+ ```
89
+
90
+ ➡️ You now have Create, Read, Update, Delete, audit, backup, and CSV endpoints at `/users` and `/db`.
91
+
92
+ ## 🛠 How-to Guides
93
+
94
+ ### Bootstrap Multiple Models
95
+
96
+ ```python
97
+ # user_accessor was created earlier in the Quickstart example
98
+ # Reuse it here to bootstrap multiple models at once
99
+
100
+ product_accessor = ModelAccessor(Product, "products")
101
+
102
+ bootstrap(
103
+ app,
104
+ accessors=[user_accessor, product_accessor],
105
+ database_url="sqlite:///./my_app.db",
106
+ create_tables=True,
107
+ max_records_per_model=100,
108
+ enable_db_router=True
109
+ )
110
+ ```
111
+
112
+ ### Export to CSV
113
+
114
+ ```python
115
+ response = requests.get("http://localhost:8000/db/export.zip")
116
+ with open("backup.zip", "wb") as f:
117
+ f.write(response.content)
118
+ ```
119
+
120
+ ### Backup & Restore
121
+
122
+ ```python
123
+ # Backup
124
+ r = requests.get("http://localhost:8000/db/backup.sqlite")
125
+ with open("backup.sqlite", "wb") as f: f.write(r.content)
126
+
127
+ # Restore
128
+ with open("backup.sqlite", "rb") as f:
129
+ files = {"file": ("backup.sqlite", f, "application/x-sqlite3")}
130
+ requests.post("http://localhost:8000/db/restore", files=files)
131
+ ```
132
+
133
+ ### Use Lifecycle Hooks
134
+
135
+ ```python
136
+ @user_accessor.before_insert()
137
+ def validate_email(session, instance):
138
+ if "@" not in instance.email:
139
+ raise ValueError("Invalid email")
140
+
141
+ @user_accessor.after_insert()
142
+ def log_creation(session, instance):
143
+ print(f"User created: {instance.name}")
144
+ ```
145
+
146
+ ### Query Audit Logs
147
+
148
+ ```python
149
+ from storage.auditor import AuditLog
150
+ from sqlmodel import select
151
+
152
+ with database.transaction() as session:
153
+ logs = session.exec(select(AuditLog).where(AuditLog.component == "users")).all()
154
+ ```
155
+
156
+ ### Using the model accessors
157
+
158
+ ```python
159
+ # Create
160
+ user = user_accessor.insert(User(name="Alice", email="alice@example.com"), actor="admin")
161
+
162
+ # Read
163
+ user = user_accessor.get(user.id)
164
+
165
+ # Update
166
+ user.name = "Alice Smith"
167
+ user_accessor.save(user, actor="admin")
168
+
169
+ # Delete
170
+ user_accessor.delete(user.id, actor="admin")
171
+
172
+ # Restore (for soft-deleted models)
173
+ user_accessor.restore(user.id, actor="admin")
174
+ ```
175
+
176
+
177
+ ### Using the REST API
178
+
179
+ Once bootstrapped, each `ModelAccessor` automatically exposes full CRUD endpoints.
180
+
181
+ Example: interacting with the `/users` API.
182
+
183
+ ```typescript
184
+ import axios from "axios";
185
+
186
+ const api = axios.create({
187
+ baseURL: "http://localhost:8000", // your backend url
188
+ headers: { "X-User": "operator" }, // used in audit logs
189
+ });
190
+
191
+ // Create
192
+ export async function createUser(name: string, email: string) {
193
+ const res = await api.post("/users/", { name, email });
194
+ return res.data;
195
+ }
196
+
197
+ // Read
198
+ export async function getUser(id: number) {
199
+ const res = await api.get(`/users/${id}`);
200
+ return res.data;
201
+ }
202
+
203
+ // Update
204
+ export async function updateUser(id: number, name: string) {
205
+ const res = await api.put(`/users/${id}`, { name });
206
+ return res.data;
207
+ }
208
+
209
+ // Delete (soft delete if model supports deleted_at)
210
+ export async function deleteUser(id: number) {
211
+ await api.delete(`/users/${id}`);
212
+ }
213
+
214
+ // Restore
215
+ export async function restoreUser(id: number) {
216
+ await api.post(`/users/${id}/restore`);
217
+ }
218
+
219
+ // List
220
+ export async function listUsers() {
221
+ const res = await api.get("/users/");
222
+ return res.data;
223
+ }
224
+ ```
225
+
226
+
227
+ ## 📖 API Reference
228
+
229
+ ### bootstrap
230
+
231
+ ```python
232
+ def bootstrap(
233
+ app: FastAPI,
234
+ *,
235
+ accessors: Iterable[ModelAccessor[Any]],
236
+ database_url: Optional[str] = None,
237
+ create_tables: bool = True,
238
+ max_records_per_model: Optional[int] = 5,
239
+ enable_db_router: bool = True,
240
+ ) -> None
241
+ ```
242
+
243
+ ### ModelAccessor
244
+
245
+ ```python
246
+ ModelAccessor(model: Type[ModelType], component_name: str)
247
+ ```
248
+
249
+ **Read**
250
+ - `get(id, include_deleted=False) -> Optional[ModelType]`
251
+ - `all(include_deleted=False) -> List[ModelType]`
252
+
253
+ **Write**
254
+ - `insert(obj, actor="internal") -> ModelType`
255
+ - `save(obj, actor="internal") -> ModelType`
256
+ - `delete(id, actor="internal") -> bool`
257
+ - `restore(id, actor="internal") -> bool`
258
+
259
+ **Batch**
260
+ - `insert_many(objs, actor="internal") -> List[ModelType]`
261
+ - `delete_many(ids, actor="internal") -> int`
262
+
263
+ **Hooks**
264
+ - `@accessor.before_insert()`
265
+ - `@accessor.after_insert()`
266
+ - `@accessor.before_update()`
267
+ - `@accessor.after_update()`
268
+ - `@accessor.before_delete()`
269
+ - `@accessor.after_delete()`
270
+
271
+ ### Routers
272
+
273
+ - `build_crud_router(accessor, max_records=100) -> APIRouter`
274
+ - `build_db_router(audit_default_limit=100, audit_max_limit=1000) -> APIRouter`
275
+
276
+ ### Database Helpers
277
+
278
+ - `database.set_database_url(url: str) -> None`
279
+ - `database.get_engine() -> Engine`
280
+ - `database.transaction() -> Iterator[Session]`
281
+ - `database.use_session(session: Optional[Session] = None) -> Iterator[Session]`
282
+
283
+ ### AuditLog model
284
+
285
+ ```python
286
+ class AuditLog(SQLModel, table=True):
287
+ id: int
288
+ timestamp: datetime
289
+ component: str
290
+ record_id: int
291
+ operation: str
292
+ actor: str
293
+ before: Optional[Dict[str, Any]]
294
+ after: Optional[Dict[str, Any]]
295
+ ```
296
+
297
+ ## 🔍 Troubleshooting & FAQ
298
+
299
+ - **Diagram endpoint fails** → Ensure Graphviz + sqlalchemy-schemadisplay are installed.
300
+ - **No audit actor shown** → Provide X-User header in API requests.
301
+ - **Soft delete not working** → Your model must have a `deleted_at` field.
302
+ - **Restore fails** → Ensure `integrity_check=True` passes when restoring backups.
@@ -0,0 +1,25 @@
1
+ [tool.poetry]
2
+ name = "vention-storage"
3
+ version = "0.5.2"
4
+ description = "A framework for storing and managing component and application data for machine apps."
5
+ authors = [ "VentionCo" ]
6
+ readme = "README.md"
7
+ license = "Proprietary"
8
+ packages = [{ include = "storage", from = "src" }]
9
+
10
+ [tool.poetry.dependencies]
11
+ sqlmodel = "0.0.27"
12
+ python = ">=3.10,<3.11"
13
+ fastapi = "0.121.1"
14
+ uvicorn = "^0.35.0"
15
+ python-multipart = "^0.0.20"
16
+
17
+ [tool.poetry.group.dev.dependencies]
18
+ pytest = "^8.3.4"
19
+ ruff = "^0.8.0"
20
+
21
+ [build-system]
22
+ requires = [
23
+ "poetry-core>=1.0.0,<2.0.0"
24
+ ]
25
+ build-backend = "poetry.core.masonry.api"