syncforge 1.0.0__tar.gz → 1.0.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.
- {syncforge-1.0.0/syncforge.egg-info → syncforge-1.0.2}/PKG-INFO +2 -3
- {syncforge-1.0.0 → syncforge-1.0.2}/pyproject.toml +3 -3
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge/client.py +56 -2
- syncforge-1.0.2/syncforge/django.py +78 -0
- {syncforge-1.0.0 → syncforge-1.0.2/syncforge.egg-info}/PKG-INFO +2 -3
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge.egg-info/SOURCES.txt +1 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/LICENSE +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/README.md +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/setup.cfg +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge/__init__.py +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge/exceptions.py +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge/result.py +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge.egg-info/dependency_links.txt +0 -0
- {syncforge-1.0.0 → syncforge-1.0.2}/syncforge.egg-info/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syncforge
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Official Python SDK for SyncForge — control exactly when data syncs between your database and clients.
|
|
5
5
|
Author-email: SyncForge <sureshdulupolai@gmail.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/sureshdulupolai/syncforge
|
|
8
8
|
Project-URL: Documentation, https://syncforge.dev/docs/
|
|
9
9
|
Project-URL: Repository, https://github.com/sureshdulupolai/syncforge
|
|
@@ -11,7 +11,6 @@ Project-URL: Bug Tracker, https://github.com/sureshdulupolai/syncforge/issues
|
|
|
11
11
|
Keywords: syncforge,sync,database,cache,invalidation,django,fastapi
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
14
|
Classifier: Programming Language :: Python :: 3
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -4,10 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "syncforge"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "Official Python SDK for SyncForge — control exactly when data syncs between your database and clients."
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
license =
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
11
12
|
authors = [
|
|
12
13
|
{ name = "SyncForge", email = "sureshdulupolai@gmail.com" }
|
|
13
14
|
]
|
|
@@ -15,7 +16,6 @@ keywords = ["syncforge", "sync", "database", "cache", "invalidation", "django",
|
|
|
15
16
|
classifiers = [
|
|
16
17
|
"Development Status :: 5 - Production/Stable",
|
|
17
18
|
"Intended Audience :: Developers",
|
|
18
|
-
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
20
|
"Programming Language :: Python :: 3.8",
|
|
21
21
|
"Programming Language :: Python :: 3.9",
|
|
@@ -157,6 +157,58 @@ class SyncForge:
|
|
|
157
157
|
data = self._request("GET", url)
|
|
158
158
|
return data.get("tables", [])
|
|
159
159
|
|
|
160
|
+
def create_table(self, table_name: str, sync_mode: str = "event") -> bool:
|
|
161
|
+
"""
|
|
162
|
+
Register a new table programmatically.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
table_name: The name of the table to register.
|
|
166
|
+
sync_mode: The sync mode (e.g. 'event', 'manual', 'schedule_5m').
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: True if created, False if it already existed.
|
|
170
|
+
"""
|
|
171
|
+
table_name = table_name.strip().lower()
|
|
172
|
+
if not table_name:
|
|
173
|
+
raise ValueError("Table name cannot be empty.")
|
|
174
|
+
|
|
175
|
+
url = f"{self._base_url}/v1/tables/"
|
|
176
|
+
try:
|
|
177
|
+
res = self._request("POST", url, json_data={"table_name": table_name, "sync_mode": sync_mode})
|
|
178
|
+
return res.get("created", False)
|
|
179
|
+
except SyncForgeError as exc:
|
|
180
|
+
if self._silent:
|
|
181
|
+
import warnings
|
|
182
|
+
warnings.warn(f"[SyncForge] create_table failed: {exc}", stacklevel=2)
|
|
183
|
+
return False
|
|
184
|
+
raise
|
|
185
|
+
|
|
186
|
+
def delete_table(self, table_name: str) -> bool:
|
|
187
|
+
"""
|
|
188
|
+
Delete a registered table from the SyncForge dashboard programmatically.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
table_name: The name of the table to delete.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
bool: True if it was deleted, False otherwise.
|
|
195
|
+
"""
|
|
196
|
+
table_name = table_name.strip().lower()
|
|
197
|
+
if not table_name:
|
|
198
|
+
raise ValueError("Table name cannot be empty.")
|
|
199
|
+
|
|
200
|
+
import urllib.parse
|
|
201
|
+
url = f"{self._base_url}/v1/tables/?table_name={urllib.parse.quote(table_name)}"
|
|
202
|
+
try:
|
|
203
|
+
res = self._request("DELETE", url)
|
|
204
|
+
return res.get("deleted", False)
|
|
205
|
+
except SyncForgeError as exc:
|
|
206
|
+
if self._silent:
|
|
207
|
+
import warnings
|
|
208
|
+
warnings.warn(f"[SyncForge] delete_table failed: {exc}", stacklevel=2)
|
|
209
|
+
return False
|
|
210
|
+
raise
|
|
211
|
+
|
|
160
212
|
# ── Internal ──────────────────────────────────────────────────────────────
|
|
161
213
|
|
|
162
214
|
def _refresh_all(self, tables: tuple) -> List[SyncResult]:
|
|
@@ -196,14 +248,16 @@ class SyncForge:
|
|
|
196
248
|
status_code = 200,
|
|
197
249
|
)
|
|
198
250
|
|
|
199
|
-
def _request(self, method: str, url: str) -> dict:
|
|
251
|
+
def _request(self, method: str, url: str, json_data: dict = None) -> dict:
|
|
200
252
|
headers = {
|
|
201
253
|
"X-API-Key": self._api_key,
|
|
202
254
|
"Content-Type": "application/json",
|
|
203
255
|
"Accept": "application/json",
|
|
204
|
-
"User-Agent": "syncforge-python/1.0.
|
|
256
|
+
"User-Agent": "syncforge-python/1.0.1",
|
|
205
257
|
}
|
|
206
258
|
body = b"" if method == "GET" else b"{}"
|
|
259
|
+
if json_data is not None:
|
|
260
|
+
body = json.dumps(json_data).encode("utf-8")
|
|
207
261
|
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
|
208
262
|
|
|
209
263
|
try:
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django integration for SyncForge.
|
|
3
|
+
Provides the @sync_model decorator to auto-sync Django models.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from django.db.models.signals import post_save, post_delete
|
|
9
|
+
from django.apps import apps
|
|
10
|
+
HAS_DJANGO = True
|
|
11
|
+
except ImportError:
|
|
12
|
+
HAS_DJANGO = False
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("syncforge")
|
|
15
|
+
|
|
16
|
+
_registered_tables = set()
|
|
17
|
+
|
|
18
|
+
def sync_model(sf_client, sync_mode='event'):
|
|
19
|
+
"""
|
|
20
|
+
Class decorator for Django models to automatically sync with SyncForge.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
from syncforge import sf
|
|
24
|
+
from syncforge.django import sync_model
|
|
25
|
+
|
|
26
|
+
@sync_model(sf)
|
|
27
|
+
class Product(models.Model):
|
|
28
|
+
name = models.CharField(max_length=100)
|
|
29
|
+
"""
|
|
30
|
+
def decorator(cls):
|
|
31
|
+
if not HAS_DJANGO:
|
|
32
|
+
raise ImportError("Django is not installed. Cannot use @sync_model.")
|
|
33
|
+
|
|
34
|
+
table_name = cls._meta.db_table
|
|
35
|
+
|
|
36
|
+
# 1. Register table on SyncForge dashboard
|
|
37
|
+
try:
|
|
38
|
+
sf_client.create_table(table_name, sync_mode=sync_mode)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.warning(f"[SyncForge] Failed to register table {table_name}: {e}")
|
|
41
|
+
|
|
42
|
+
_registered_tables.add(table_name)
|
|
43
|
+
|
|
44
|
+
# 2. Hook into ORM signals to trigger syncs automatically
|
|
45
|
+
def _trigger_sync(sender, **kwargs):
|
|
46
|
+
try:
|
|
47
|
+
sf_client.refresh(table_name)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error(f"[SyncForge] Failed to trigger sync for {table_name}: {e}")
|
|
50
|
+
|
|
51
|
+
# Connect signals
|
|
52
|
+
post_save.connect(_trigger_sync, sender=cls, weak=False, dispatch_uid=f"sf_save_{table_name}")
|
|
53
|
+
post_delete.connect(_trigger_sync, sender=cls, weak=False, dispatch_uid=f"sf_delete_{table_name}")
|
|
54
|
+
|
|
55
|
+
return cls
|
|
56
|
+
return decorator
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def sync_migrations(sf_client):
|
|
60
|
+
"""
|
|
61
|
+
Removes tables from the SyncForge dashboard that no longer exist in your Django project.
|
|
62
|
+
Call this inside an AppConfig.ready() or after your migrations run.
|
|
63
|
+
"""
|
|
64
|
+
if not HAS_DJANGO:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
active_tables = {model._meta.db_table for model in apps.get_models()}
|
|
69
|
+
|
|
70
|
+
# Fetch current registered tables from SyncForge
|
|
71
|
+
sf_tables = sf_client.list_tables()
|
|
72
|
+
for t in sf_tables:
|
|
73
|
+
t_name = t.get('table_name')
|
|
74
|
+
if t_name and t_name not in active_tables:
|
|
75
|
+
sf_client.delete_table(t_name)
|
|
76
|
+
logger.info(f"[SyncForge] Cleaned up deleted table: {t_name}")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.warning(f"[SyncForge] sync_migrations cleanup failed: {e}")
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syncforge
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Official Python SDK for SyncForge — control exactly when data syncs between your database and clients.
|
|
5
5
|
Author-email: SyncForge <sureshdulupolai@gmail.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/sureshdulupolai/syncforge
|
|
8
8
|
Project-URL: Documentation, https://syncforge.dev/docs/
|
|
9
9
|
Project-URL: Repository, https://github.com/sureshdulupolai/syncforge
|
|
@@ -11,7 +11,6 @@ Project-URL: Bug Tracker, https://github.com/sureshdulupolai/syncforge/issues
|
|
|
11
11
|
Keywords: syncforge,sync,database,cache,invalidation,django,fastapi
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
14
|
Classifier: Programming Language :: Python :: 3
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|