ciyidogan commited on
Commit
1b4f3f8
Β·
verified Β·
1 Parent(s): 67cc066

Create api_executor.py

Browse files
Files changed (1) hide show
  1. api_executor.py +76 -0
api_executor.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Flare – API Executor
3
+ ~~~~~~~~~~~~~~~~~~~~
4
+ β€’ header/body templating
5
+ β€’ auth token caching + refresh
6
+ β€’ retry w/ tenacity
7
+ """
8
+
9
+ import json
10
+ import re
11
+ import time
12
+ from typing import Dict, Any
13
+
14
+ import httpx
15
+ from tenacity import retry, stop_after_attempt, wait_fixed, wait_exponential
16
+
17
+ from config_provider import APIConfig
18
+ from log_utils import log
19
+
20
+ _TOKEN_CACHE: Dict[str, Dict[str, Any]] = {} # name -> {token, expiry}
21
+
22
+
23
+ def _apply_template(template: Dict[str, str],
24
+ variables: Dict[str, str]) -> Dict[str, str]:
25
+ out = {}
26
+ for k, v in template.items():
27
+ out[k] = re.sub(r"{{\s*([a-zA-Z0-9_.]+)\s*}}",
28
+ lambda m: variables.get(m.group(1), ""), v)
29
+ return out
30
+
31
+
32
+ def _get_auth_token(name: str, api_cfg: APIConfig) -> str:
33
+ if not api_cfg.auth.enabled:
34
+ return ""
35
+
36
+ cached = _TOKEN_CACHE.get(name)
37
+ if cached and cached["expiry"] > time.time():
38
+ return cached["token"]
39
+
40
+ log(f"πŸ”‘ Fetching token for API [{name}] ...")
41
+ body = _apply_template(api_cfg.auth.body_template, {})
42
+ resp = httpx.post(api_cfg.auth.token_endpoint, json=body, timeout=10)
43
+ resp.raise_for_status()
44
+ token = resp.json()
45
+ for segment in api_cfg.auth.response_token_path.split("."):
46
+ token = token.get(segment)
47
+ _TOKEN_CACHE[name] = {"token": token, "expiry": time.time() + 3000}
48
+ return token
49
+
50
+
51
+ def _tenacity_wait(cfg: APIConfig):
52
+ return (wait_exponential(multiplier=cfg.retry.backoff_seconds)
53
+ if cfg.retry.strategy == "exponential"
54
+ else wait_fixed(cfg.retry.backoff_seconds))
55
+
56
+
57
+ @retry(stop=stop_after_attempt(lambda cfg: cfg.retry.max_attempts),
58
+ wait=lambda retry_state: _tenacity_wait(retry_state.args[0]))
59
+ def call_api(cfg: APIConfig,
60
+ variables: Dict[str, str]) -> httpx.Response:
61
+ headers = _apply_template(cfg.headers, {"auth_tokens": _TOKEN_CACHE,
62
+ **variables})
63
+ body = _apply_template(cfg.body_template, {"variables": variables})
64
+
65
+ if cfg.auth.enabled:
66
+ headers["Authorization"] = f"Bearer {_get_auth_token(cfg.url, cfg)}"
67
+
68
+ log(f"🌐 Calling API {cfg.method} {cfg.url}")
69
+ client_args = {"timeout": cfg.timeout_seconds}
70
+ if cfg.proxy.enabled:
71
+ client_args["proxies"] = {"all://": cfg.proxy.url}
72
+
73
+ with httpx.Client(**client_args) as client:
74
+ resp = client.request(cfg.method, cfg.url, json=body, headers=headers)
75
+ resp.raise_for_status()
76
+ return resp