Spaces:
Building
Building
Update api_executor.py
Browse files- api_executor.py +83 -65
api_executor.py
CHANGED
@@ -1,76 +1,94 @@
|
|
1 |
"""
|
2 |
Flare – API Executor
|
3 |
~~~~~~~~~~~~~~~~~~~~
|
4 |
-
•
|
5 |
-
•
|
6 |
-
•
|
7 |
"""
|
8 |
|
|
|
|
|
9 |
import json
|
10 |
import re
|
11 |
import time
|
12 |
-
from typing import
|
13 |
-
|
14 |
-
import httpx
|
15 |
-
from tenacity import retry, stop_after_attempt, wait_fixed, wait_exponential
|
16 |
|
17 |
-
|
18 |
from utils import log
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
if cached and cached["expiry"] > time.time():
|
38 |
-
return
|
39 |
-
|
40 |
-
|
41 |
-
body =
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
1 |
"""
|
2 |
Flare – API Executor
|
3 |
~~~~~~~~~~~~~~~~~~~~
|
4 |
+
• Placeholder yerleştirme ({{variables.x}}, {{auth_tokens.api.token}}, {{config.xxx}})
|
5 |
+
• Proxy string veya ProxyConfig objesi
|
6 |
+
• Auth: enabled==True → token alma & cache
|
7 |
"""
|
8 |
|
9 |
+
from __future__ import annotations
|
10 |
+
|
11 |
import json
|
12 |
import re
|
13 |
import time
|
14 |
+
from typing import Any, Dict
|
|
|
|
|
|
|
15 |
|
16 |
+
import requests
|
17 |
from utils import log
|
18 |
+
from config_provider import ConfigProvider, APIConfig, ProxyConfig
|
19 |
+
|
20 |
+
cfg = ConfigProvider.get()
|
21 |
+
_token_cache: Dict[str, Dict[str, Any]] = {} # api_name → {token, expiry}
|
22 |
+
|
23 |
+
|
24 |
+
# ----------- Placeholder helpers ----------
|
25 |
+
_placeholder_re = re.compile(r"\{\{\s*([^\}]+?)\s*\}\}")
|
26 |
+
|
27 |
+
|
28 |
+
def _render(obj: Any, variables: Dict[str, Any], api_name: str) -> Any:
|
29 |
+
def repl(match):
|
30 |
+
key = match.group(1)
|
31 |
+
if key.startswith("variables."):
|
32 |
+
return str(variables.get(key.split(".", 1)[1], ""))
|
33 |
+
if key.startswith("auth_tokens."):
|
34 |
+
_, api, _ = key.split(".")
|
35 |
+
return _token_cache.get(api, {}).get("token", "")
|
36 |
+
if key.startswith("config."):
|
37 |
+
_, prop = key.split(".", 1)
|
38 |
+
return str(getattr(cfg.global_config, prop, ""))
|
39 |
+
return match.group(0)
|
40 |
+
|
41 |
+
if isinstance(obj, str):
|
42 |
+
return _placeholder_re.sub(repl, obj)
|
43 |
+
if isinstance(obj, dict):
|
44 |
+
return {k: _render(v, variables, api_name) for k, v in obj.items()}
|
45 |
+
if isinstance(obj, list):
|
46 |
+
return [_render(v, variables, api_name) for v in obj]
|
47 |
+
return obj
|
48 |
+
|
49 |
+
|
50 |
+
# ----------- Auth helpers -----------------
|
51 |
+
def _ensure_token(api: APIConfig):
|
52 |
+
if not api.auth or not api.auth.enabled:
|
53 |
+
return
|
54 |
+
cached = _token_cache.get(api.name)
|
55 |
if cached and cached["expiry"] > time.time():
|
56 |
+
return
|
57 |
+
|
58 |
+
body = api.auth.token_request_body
|
59 |
+
body = _render(body, {}, api.name)
|
60 |
+
log(f"🔒 Fetching token for {api.name} …")
|
61 |
+
r = requests.post(api.auth.token_endpoint, json=body, timeout=api.timeout_seconds)
|
62 |
+
r.raise_for_status()
|
63 |
+
js = r.json()
|
64 |
+
token = js
|
65 |
+
for part in api.auth.response_token_path.split("."):
|
66 |
+
token = token[part]
|
67 |
+
_token_cache[api.name] = {"token": token, "expiry": time.time() + 3500}
|
68 |
+
|
69 |
+
|
70 |
+
# ----------- Main call --------------------
|
71 |
+
def call_api(api: APIConfig, variables: Dict[str, Any]):
|
72 |
+
_ensure_token(api)
|
73 |
+
|
74 |
+
headers = _render(api.headers, variables, api.name)
|
75 |
+
body = _render(api.body_template, variables, api.name)
|
76 |
+
url = api.url
|
77 |
+
method = api.method.upper()
|
78 |
+
|
79 |
+
proxy = None
|
80 |
+
if api.proxy:
|
81 |
+
proxy = api.proxy if isinstance(api.proxy, str) else (api.proxy.url if api.proxy.enabled else None)
|
82 |
+
|
83 |
+
log(f"🌐 {api.name} → {method} {url}")
|
84 |
+
r = requests.request(
|
85 |
+
method,
|
86 |
+
url,
|
87 |
+
json=body if method in ("POST", "PUT", "PATCH") else None,
|
88 |
+
params=body if method == "GET" else None,
|
89 |
+
headers=headers,
|
90 |
+
proxies={"http": proxy, "https": proxy} if proxy else None,
|
91 |
+
timeout=api.timeout_seconds,
|
92 |
+
)
|
93 |
+
r.raise_for_status()
|
94 |
+
return r
|