feedple-sdk 0.0.1__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.
- feedple_sdk-0.0.1/.gitignore +79 -0
- feedple_sdk-0.0.1/LICENSE.txt +18 -0
- feedple_sdk-0.0.1/PKG-INFO +427 -0
- feedple_sdk-0.0.1/README.md +402 -0
- feedple_sdk-0.0.1/cmd.ps1 +17 -0
- feedple_sdk-0.0.1/docs/usage.md +635 -0
- feedple_sdk-0.0.1/pyproject.toml +115 -0
- feedple_sdk-0.0.1/src/feedple_sdk/__about__.py +4 -0
- feedple_sdk-0.0.1/src/feedple_sdk/__init__.py +176 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/compiler.py +30 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/ir_builder.py +161 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/json.py +22 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/policy.py +29 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/request.py +50 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/schema_services.py +151 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/websocket.py +290 -0
- feedple_sdk-0.0.1/src/feedple_sdk/core/ws_schema.py +236 -0
- feedple_sdk-0.0.1/src/feedple_sdk/tests/__init__.py +3 -0
- feedple_sdk-0.0.1/src/feedple_sdk/tests/test_sdk.py +13 -0
- feedple_sdk-0.0.1/src/feedple_sdk/tests/test_websocket.py +31 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# =========================
|
|
2
|
+
# Python bytecode
|
|
3
|
+
# =========================
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.pyo
|
|
7
|
+
*.pyd
|
|
8
|
+
|
|
9
|
+
# =========================
|
|
10
|
+
# Virtual environments
|
|
11
|
+
# =========================
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
env/
|
|
15
|
+
ENV/
|
|
16
|
+
|
|
17
|
+
# pyenv
|
|
18
|
+
.python-version
|
|
19
|
+
|
|
20
|
+
# Hatch / virtualenv artifacts
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.hatch/
|
|
23
|
+
.hatch.env/
|
|
24
|
+
.hatch_build/
|
|
25
|
+
|
|
26
|
+
# =========================
|
|
27
|
+
# Build / packaging
|
|
28
|
+
# =========================
|
|
29
|
+
build/
|
|
30
|
+
dist/
|
|
31
|
+
*.egg-info/
|
|
32
|
+
.eggs/
|
|
33
|
+
|
|
34
|
+
# =========================
|
|
35
|
+
# Testing
|
|
36
|
+
# =========================
|
|
37
|
+
.coverage
|
|
38
|
+
coverage.xml
|
|
39
|
+
htmlcov/
|
|
40
|
+
|
|
41
|
+
# =========================
|
|
42
|
+
# MyPy / type checking
|
|
43
|
+
# =========================
|
|
44
|
+
.mypy_cache/
|
|
45
|
+
.dmypy.json
|
|
46
|
+
|
|
47
|
+
# =========================
|
|
48
|
+
# Ruff / lint caches
|
|
49
|
+
# =========================
|
|
50
|
+
.ruff_cache/
|
|
51
|
+
|
|
52
|
+
# =========================
|
|
53
|
+
# Pytest
|
|
54
|
+
# =========================
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
|
|
57
|
+
# =========================
|
|
58
|
+
# OS files
|
|
59
|
+
# =========================
|
|
60
|
+
.DS_Store
|
|
61
|
+
Thumbs.db
|
|
62
|
+
|
|
63
|
+
# =========================
|
|
64
|
+
# IDEs
|
|
65
|
+
# =========================
|
|
66
|
+
.vscode/
|
|
67
|
+
.idea/
|
|
68
|
+
|
|
69
|
+
# =========================
|
|
70
|
+
# Logs
|
|
71
|
+
# =========================
|
|
72
|
+
*.log
|
|
73
|
+
|
|
74
|
+
# =========================
|
|
75
|
+
# Environment variables
|
|
76
|
+
# =========================
|
|
77
|
+
.env
|
|
78
|
+
.env.*
|
|
79
|
+
!.env.example
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Umar Bello Kanwa <umarbellokanwa@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: feedple-sdk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python SDK for Feedple AI
|
|
5
|
+
Project-URL: Documentation, https://github.com/Umar Bello Kanwa/feedple-sdk#readme
|
|
6
|
+
Project-URL: Issues, https://github.com/Umar Bello Kanwa/feedple-sdk/issues
|
|
7
|
+
Project-URL: Source, https://github.com/Umar Bello Kanwa/feedple-sdk
|
|
8
|
+
Author-email: Umar Bello Kanwa <umarbellokanwa@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Requires-Dist: sqlalchemy
|
|
22
|
+
Requires-Dist: sqlglot
|
|
23
|
+
Requires-Dist: websockets
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Feedple Python SDK
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/feedple-sdk)
|
|
29
|
+
[](https://pypi.org/project/feedple-sdk)
|
|
30
|
+
[](https://spdx.org/licenses/MIT.html)
|
|
31
|
+
|
|
32
|
+
The official Python SDK for the [Feedple AI](https://feedple.ai) platform. Connect your database to Feedple with a single class — the SDK handles authentication, schema sync, and query execution automatically.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Table of Contents
|
|
37
|
+
|
|
38
|
+
- [How It Works](#how-it-works)
|
|
39
|
+
- [Requirements](#requirements)
|
|
40
|
+
- [Installation](#installation)
|
|
41
|
+
- [Quick Start](#quick-start)
|
|
42
|
+
- [Configuration](#configuration)
|
|
43
|
+
- [Identity & Access Control](#identity--access-control)
|
|
44
|
+
- [Schema Sync](#schema-sync)
|
|
45
|
+
- [IR Query Execution](#ir-query-execution)
|
|
46
|
+
- [Connection Management](#connection-management)
|
|
47
|
+
- [Utilities](#utilities)
|
|
48
|
+
- [API Reference](#api-reference)
|
|
49
|
+
- [License](#license)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## How It Works
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Your App Feedple SDK Feedple API
|
|
57
|
+
│ │ │
|
|
58
|
+
│ FeedpleSDK(api_key, db, …) │ │
|
|
59
|
+
│─────────────────────────────>│ │
|
|
60
|
+
│ │── connect() ─────────────────>│
|
|
61
|
+
│ │<─ auth.ack (session_id) ──────│
|
|
62
|
+
│ │ │
|
|
63
|
+
│ │── schema.started ────────────>│
|
|
64
|
+
│ │── schema.data (chunks) ──────>│
|
|
65
|
+
│ │── schema.completed ──────────>│
|
|
66
|
+
│ │ │
|
|
67
|
+
│ │<─ ir.request (IR payload) ────│
|
|
68
|
+
│ │── ir.ack ────────────────────>│
|
|
69
|
+
│ │ [executes SQL against DB] │
|
|
70
|
+
│ │── ir.result ─────────────────>│
|
|
71
|
+
│ │ │
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
1. **Connect & Authenticate** — Opens a persistent WebSocket connection and sends your API key. The server responds with a `session_id` used to resume after disconnects.
|
|
75
|
+
2. **Sync Schema** — Inspects your database (tables, columns, PKs, FKs, indexes) and sends the schema to Feedple in chunks. Re-syncs on a configurable interval; skips if nothing changed.
|
|
76
|
+
3. **Execute Queries** — Receives incoming IR query requests from Feedple, enforces RBAC, executes them safely against your database, and returns the results.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Requirements
|
|
81
|
+
|
|
82
|
+
- Python **≥ 3.8**
|
|
83
|
+
- A database supported by **SQLAlchemy** (PostgreSQL, MySQL, SQLite, and others)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install feedple-sdk
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Install with a specific database driver:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# PostgreSQL
|
|
97
|
+
pip install feedple-sdk psycopg2-binary
|
|
98
|
+
|
|
99
|
+
# MySQL
|
|
100
|
+
pip install feedple-sdk pymysql
|
|
101
|
+
|
|
102
|
+
# SQLite (built into Python — no extra driver needed)
|
|
103
|
+
pip install feedple-sdk
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Quick Start
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from sqlalchemy import create_engine
|
|
112
|
+
from feedple_sdk import FeedpleSDK, Identity
|
|
113
|
+
|
|
114
|
+
# 1. Create your database engine (SQLAlchemy)
|
|
115
|
+
db = create_engine("postgresql+psycopg2://user:pass@localhost/mydb")
|
|
116
|
+
|
|
117
|
+
# 2. Define which tables Feedple can access
|
|
118
|
+
identity = Identity(
|
|
119
|
+
name="production",
|
|
120
|
+
all_tables=True, # expose every table, or…
|
|
121
|
+
# allowed_tables=["users", "orders", "products"], # …restrict to specific tables
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# 3. Initialise the SDK — it starts immediately in the background
|
|
125
|
+
sdk = FeedpleSDK(
|
|
126
|
+
api_key="sk_live_...",
|
|
127
|
+
db=db,
|
|
128
|
+
identity=identity,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Your application keeps running normally.
|
|
132
|
+
# The SDK manages the WebSocket connection on a background thread.
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
> **Note:** `FeedpleSDK.__init__` starts a background daemon thread and returns immediately. Your application does not block.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
All parameters are keyword-only.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
sdk = FeedpleSDK(
|
|
145
|
+
# Required
|
|
146
|
+
api_key="sk_live_...", # Your Feedple API key
|
|
147
|
+
db=engine, # SQLAlchemy Engine
|
|
148
|
+
identity=identity, # Identity (see below)
|
|
149
|
+
|
|
150
|
+
# Schema sync
|
|
151
|
+
auto_sync=True, # Sync schema on startup and periodically (default: True)
|
|
152
|
+
sync_interval=60, # Seconds between sync cycles (default: 60)
|
|
153
|
+
|
|
154
|
+
# Connection
|
|
155
|
+
reconnect_enabled=True, # Reconnect on disconnect (default: True)
|
|
156
|
+
max_retries=None, # Max reconnect attempts; None = unlimited (default: None)
|
|
157
|
+
probe_before_connect=False, # HTTP probe before WS handshake for clearer errors (default: False)
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Parameter Reference
|
|
162
|
+
|
|
163
|
+
| Parameter | Type | Default | Description |
|
|
164
|
+
|-----------|------|---------|-------------|
|
|
165
|
+
| `api_key` | `str` | **required** | Your Feedple API key. Raises `ValueError` if empty. |
|
|
166
|
+
| `db` | `Engine` | **required** | SQLAlchemy database engine. |
|
|
167
|
+
| `identity` | `Identity` | **required** | Controls which tables are accessible. |
|
|
168
|
+
| `auto_sync` | `bool` | `True` | Periodically re-inspect and send the schema. |
|
|
169
|
+
| `sync_interval` | `int` | `60` | Seconds between schema re-sync cycles. |
|
|
170
|
+
| `reconnect_enabled` | `bool` | `True` | Automatically reconnect on connection loss. |
|
|
171
|
+
| `max_retries` | `int \| None` | `None` | Cap on reconnect attempts. `None` means unlimited. |
|
|
172
|
+
| `probe_before_connect` | `bool` | `False` | Perform an HTTP GET before the WS handshake to surface clearer server error messages (e.g. 403 bodies). |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Identity & Access Control
|
|
177
|
+
|
|
178
|
+
`Identity` controls which database tables Feedple can see and query.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from feedple_sdk import Identity
|
|
182
|
+
|
|
183
|
+
# Grant access to all tables
|
|
184
|
+
admin_identity = Identity(
|
|
185
|
+
name="admin",
|
|
186
|
+
all_tables=True,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Restrict to specific tables only
|
|
190
|
+
restricted_identity = Identity(
|
|
191
|
+
name="analytics-service",
|
|
192
|
+
allowed_tables=["users", "orders", "products", "events"],
|
|
193
|
+
all_tables=False, # default
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Identity Fields
|
|
198
|
+
|
|
199
|
+
| Field | Type | Default | Description |
|
|
200
|
+
|-------|------|---------|-------------|
|
|
201
|
+
| `name` | `str \| None` | — | Human-readable label for this identity. |
|
|
202
|
+
| `allowed_tables` | `list[str]` | `[]` | Tables this identity may access. Ignored when `all_tables=True`. |
|
|
203
|
+
| `all_tables` | `bool` | `False` | When `True`, all current and future tables are accessible. |
|
|
204
|
+
|
|
205
|
+
> **Security:** The SDK enforces RBAC on every IR query request. If the IR references a table not in `allowed_tables`, a `PermissionError` is raised and an `ir.error` is returned to the server — the query never reaches the database.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Schema Sync
|
|
210
|
+
|
|
211
|
+
The SDK automatically syncs your schema on startup and then every `sync_interval` seconds. You can also trigger a sync manually:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
import asyncio
|
|
215
|
+
|
|
216
|
+
# Manually trigger a schema sync (async — must be called from an async context)
|
|
217
|
+
asyncio.run(sdk.sync_schema())
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### What gets synced
|
|
221
|
+
|
|
222
|
+
For each table the identity can access, the SDK sends:
|
|
223
|
+
|
|
224
|
+
- **Columns** — name, type string, nullable flag, default value
|
|
225
|
+
- **Primary key** — constrained column names
|
|
226
|
+
- **Foreign keys** — local columns, referenced table, referenced columns
|
|
227
|
+
- **Indexes** — name, column names, unique flag
|
|
228
|
+
- **Unique constraints** — name, column names
|
|
229
|
+
|
|
230
|
+
### Sensitive column filtering
|
|
231
|
+
|
|
232
|
+
The following column names are **never sent** to Feedple, regardless of the identity setting:
|
|
233
|
+
|
|
234
|
+
`password`, `token`, `secret`, `hash`, `salt`, `ssn`, `credit_card`
|
|
235
|
+
|
|
236
|
+
You can call `filter_sensitive_columns` manually:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from feedple_sdk.core.schema_services import filter_sensitive_columns
|
|
240
|
+
|
|
241
|
+
safe_schema = filter_sensitive_columns(raw_schema)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## IR Query Execution
|
|
247
|
+
|
|
248
|
+
The SDK receives IR (Intermediate Representation) query objects from the Feedple server and executes them against your database. You do not call this yourself — it is invoked automatically via the WebSocket.
|
|
249
|
+
|
|
250
|
+
### IR Schema
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
ir = {
|
|
254
|
+
"operation": "query", # always "query"
|
|
255
|
+
"table": "orders", # primary FROM table
|
|
256
|
+
"fields": [ # columns to SELECT
|
|
257
|
+
{"column": "orders.id", "expression": None, "alias": None},
|
|
258
|
+
{"column": "orders.amount", "expression": "sum", "alias": "total"},
|
|
259
|
+
{"column": "orders.user_id","expression": "count(distinct)", "alias": "unique_users"},
|
|
260
|
+
],
|
|
261
|
+
"joins": [ # JOIN clauses
|
|
262
|
+
{
|
|
263
|
+
"table": "users",
|
|
264
|
+
"on_left": "orders.user_id",
|
|
265
|
+
"on_right": "users.id",
|
|
266
|
+
"join_type": "INNER", # "INNER" or "LEFT"
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
"filters": [ # WHERE conditions (ANDed together)
|
|
270
|
+
{"column": "orders.status", "operator": "eq", "value": "active"},
|
|
271
|
+
{"column": "orders.amount", "operator": "gte", "value": 100},
|
|
272
|
+
],
|
|
273
|
+
"group_by": ["orders.status"],
|
|
274
|
+
"having": [{"column": "orders.id", "operator": "gt", "value": 5}],
|
|
275
|
+
"order_by": ["orders.created_at DESC"],
|
|
276
|
+
"limit": 100,
|
|
277
|
+
"offset": 0,
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Supported filter operators
|
|
282
|
+
|
|
283
|
+
| Operator | Aliases | SQL |
|
|
284
|
+
|----------|---------|-----|
|
|
285
|
+
| `eq` | `=` | `col = ?` |
|
|
286
|
+
| `neq` | `!=` | `col != ?` |
|
|
287
|
+
| `gt` | `>` | `col > ?` |
|
|
288
|
+
| `gte` | `>=` | `col >= ?` |
|
|
289
|
+
| `lt` | `<` | `col < ?` |
|
|
290
|
+
| `lte` | `<=` | `col <= ?` |
|
|
291
|
+
| `in` | `in_` | `col IN (?, ?, …)` |
|
|
292
|
+
| `like` | — | `col LIKE ?` |
|
|
293
|
+
| `ilike` | — | `col ILIKE ?` |
|
|
294
|
+
|
|
295
|
+
### Supported aggregate expressions
|
|
296
|
+
|
|
297
|
+
`count`, `count(distinct)`, `sum`, `avg`, `min`, `max`
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Connection Management
|
|
302
|
+
|
|
303
|
+
### Reconnect behaviour
|
|
304
|
+
|
|
305
|
+
The SDK reconnects automatically with exponential back-off:
|
|
306
|
+
|
|
307
|
+
| Attempt | Delay |
|
|
308
|
+
|---------|-------|
|
|
309
|
+
| 1 | 5 s |
|
|
310
|
+
| 2 | 10 s |
|
|
311
|
+
| 3 | 20 s |
|
|
312
|
+
| 4+ | 40 s → capped at 60 s |
|
|
313
|
+
|
|
314
|
+
Authentication failures (`auth.error`) are **not** retried — the SDK stops immediately and logs the error.
|
|
315
|
+
|
|
316
|
+
### Session resume
|
|
317
|
+
|
|
318
|
+
The `session_id` received in `auth.ack` is stored and re-sent on every reconnect attempt. The server resumes the session if it is still within TTL, or issues a new session ID if it has expired.
|
|
319
|
+
|
|
320
|
+
### Stopping the SDK
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
sdk.stop()
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Signals the background thread to stop, closes the WebSocket, and halts the event loop. Idempotent — safe to call multiple times.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Utilities
|
|
331
|
+
|
|
332
|
+
### SQLCompiler
|
|
333
|
+
|
|
334
|
+
Validate and RBAC-check raw SQL strings before executing them yourself:
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
compiler = sdk._build_compiler()
|
|
338
|
+
|
|
339
|
+
# Raises PermissionError if the SQL references a denied table
|
|
340
|
+
safe_sql = compiler.compile("SELECT id, name FROM users WHERE active = 1")
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Schema utilities
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
from feedple_sdk.core.schema_services import (
|
|
347
|
+
get_schema,
|
|
348
|
+
generate_schema_hash,
|
|
349
|
+
should_sync_schema,
|
|
350
|
+
filter_sensitive_columns,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
schema = get_schema(db=engine, identity=identity)
|
|
354
|
+
hash_ = generate_schema_hash(schema)
|
|
355
|
+
changed = should_sync_schema(old_schema, new_schema)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## API Reference
|
|
361
|
+
|
|
362
|
+
### `FeedpleSDK`
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
class FeedpleSDK:
|
|
366
|
+
def __init__(
|
|
367
|
+
self,
|
|
368
|
+
*,
|
|
369
|
+
api_key: str,
|
|
370
|
+
db: Engine,
|
|
371
|
+
identity: Identity,
|
|
372
|
+
auto_sync: bool = True,
|
|
373
|
+
sync_interval: int = 60,
|
|
374
|
+
reconnect_enabled: bool = True,
|
|
375
|
+
max_retries: Optional[int] = None,
|
|
376
|
+
probe_before_connect: bool = False,
|
|
377
|
+
) -> None: ...
|
|
378
|
+
|
|
379
|
+
async def sync_schema(self) -> None: ...
|
|
380
|
+
def stop(self) -> None: ...
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### `Identity`
|
|
384
|
+
|
|
385
|
+
```python
|
|
386
|
+
@dataclass
|
|
387
|
+
class Identity:
|
|
388
|
+
name: Optional[str]
|
|
389
|
+
allowed_tables: List[str] = field(default_factory=list)
|
|
390
|
+
all_tables: bool = False
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### `PolicyEngine`
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
class PolicyEngine:
|
|
397
|
+
def __init__(self, identity: Identity): ...
|
|
398
|
+
def can_access_table(self, table: str) -> bool: ...
|
|
399
|
+
def validate_ir_access(self, ir: dict) -> None: ...
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### `SQLCompiler`
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
class SQLCompiler:
|
|
406
|
+
def __init__(self, policy: PolicyEngine, dialect: str = "postgres"): ...
|
|
407
|
+
def parse(self, sql: str) -> exp.Expression: ...
|
|
408
|
+
def extract_tables(self, ast: exp.Expression) -> List[str]: ...
|
|
409
|
+
def validate_access(self, tables: List[str]) -> None: ...
|
|
410
|
+
def compile(self, sql: str) -> str: ...
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Environment URLs
|
|
416
|
+
|
|
417
|
+
By default the SDK targets the **development** server (`localhost:8000`). To target production, set `DEV_ENV = False` in `src/feedple_sdk/core/request.py`:
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
DEV_ENV = False # uses wss://feedple-ai.onrender.com/api/v1/tenants/ws
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
`feedple-sdk` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|