botversion-sdk 2.1.0__tar.gz → 2.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: botversion-sdk
3
- Version: 2.1.0
3
+ Version: 2.2.1
4
4
  Summary: BotVersion SDK — automatically discover and register your API endpoints
5
5
  Home-page: https://github.com/botversion/botversion-sdk-python
6
6
  Author: BotVersion
@@ -158,7 +158,7 @@ def init(app=None, api_key=None, **options):
158
158
 
159
159
  _client = BotVersionClient({
160
160
  "api_key": api_key,
161
- "platform_url": options.get("platform_url", "https://console.botversion.com"),
161
+ "platform_url": options.get("platform_url", "http://localhost:3000"),
162
162
  "debug": debug,
163
163
  "timeout": options.get("timeout", 30),
164
164
  "flush_delay": options.get("flush_delay", 3),
@@ -28,7 +28,7 @@ def main():
28
28
  _load_env_files(cwd)
29
29
 
30
30
  api_key = os.environ.get("BOTVERSION_API_KEY")
31
- platform_url = os.environ.get("BOTVERSION_PLATFORM_URL", "https://console.botversion.com")
31
+ platform_url = os.environ.get("BOTVERSION_PLATFORM_URL", "http://localhost:3000")
32
32
 
33
33
  if not api_key:
34
34
  print("[botversion] BOTVERSION_API_KEY environment variable is not set. Skipping backend endpoint scan.")
@@ -6,12 +6,13 @@ import urllib.parse
6
6
  import urllib.error
7
7
  import atexit
8
8
  import time
9
+ from collections import deque
9
10
 
10
11
  class BotVersionClient:
11
12
 
12
13
  def __init__(self, options):
13
14
  self.api_key = options["api_key"]
14
- platform_url = options.get("platform_url", "https://console.botversion.com")
15
+ platform_url = options.get("platform_url", "http://localhost:3000")
15
16
 
16
17
  self.platform_url = platform_url
17
18
  self.debug = options.get("debug", False)
@@ -24,6 +25,10 @@ class BotVersionClient:
24
25
  self._lock = threading.Lock()
25
26
  atexit.register(self._flush)
26
27
 
28
+ # Per-endpoint send queue: updates for the same endpoint go out one after another
29
+ self._pending_updates = {}
30
+ self._pending_lock = threading.Lock()
31
+
27
32
  # ── Register endpoints (batched) ─────────────────────────────────────────
28
33
 
29
34
  def register_endpoints(self, endpoints):
@@ -73,18 +78,52 @@ class BotVersionClient:
73
78
  # ── Update single endpoint (runtime interceptor) ─────────────────────────
74
79
 
75
80
  def update_endpoint(self, endpoint):
81
+ payload = {
82
+ "workspaceKey": self.api_key,
83
+ "method": endpoint.get("method"),
84
+ "path": endpoint.get("path"),
85
+ "requestBody": endpoint.get("request_body"),
86
+ "responseBody": endpoint.get("response_body"),
87
+ "detectedBy": endpoint.get("detected_by", "runtime"),
88
+ }
89
+
90
+ # Queued here, in the caller's thread, so updates for one endpoint always go out
91
+ # in the order they were requested (prevents request/reply ordering races).
92
+ key = f"{str(endpoint.get('method')).upper()}:{endpoint.get('path')}"
93
+ with self._pending_lock:
94
+ queue = self._pending_updates.get(key)
95
+ if queue is not None:
96
+ queue.append(payload) # a sender is already running for this endpoint
97
+ return
98
+ self._pending_updates[key] = deque([payload])
99
+
76
100
  try:
77
- self._post("/api/sdk/update-endpoint", {
78
- "workspaceKey": self.api_key,
79
- "method": endpoint.get("method"),
80
- "path": endpoint.get("path"),
81
- "requestBody": endpoint.get("request_body"),
82
- "responseBody": endpoint.get("response_body"),
83
- "detectedBy": endpoint.get("detected_by", "runtime"),
84
- })
85
- except Exception as e:
86
- if self.debug:
87
- print(f"[botversion] update_endpoint failed: {e}")
101
+ threading.Thread(target=self._drain_updates, args=(key,), daemon=True).start()
102
+ except Exception:
103
+ # If no sender could start, clear the key so later updates are not stuck behind it
104
+ with self._pending_lock:
105
+ self._pending_updates.pop(key, None)
106
+
107
+ def _drain_updates(self, key):
108
+ # Sends queued updates for one endpoint, one after another, off the request path
109
+ while True:
110
+ with self._pending_lock:
111
+ queue = self._pending_updates.get(key)
112
+ if not queue:
113
+ self._pending_updates.pop(key, None)
114
+ return
115
+ next_payload = queue.popleft()
116
+ try:
117
+ self._post("/api/sdk/update-endpoint", next_payload)
118
+ if self.debug:
119
+ print(
120
+ f"[botversion] sent update {key} "
121
+ f"(request={bool(next_payload.get('requestBody'))}, "
122
+ f"response={bool(next_payload.get('responseBody'))})"
123
+ )
124
+ except Exception as e:
125
+ if self.debug:
126
+ print(f"[botversion] update_endpoint failed: {e}")
88
127
 
89
128
 
90
129
  # ── Register frontend route patterns ─────────────────────────────────────────