ciyidogan commited on
Commit
70e75db
Β·
verified Β·
1 Parent(s): e71d582

Update api_executor.py

Browse files
Files changed (1) hide show
  1. api_executor.py +90 -82
api_executor.py CHANGED
@@ -4,7 +4,7 @@ Flare – API Executor (v2.0 Β· session-aware token management)
4
 
5
  from __future__ import annotations
6
  import json, re, time, requests
7
- from typing import Any, Dict, Optional
8
  from utils import log
9
  from config_provider import ConfigProvider, APIConfig
10
  from session import Session
@@ -13,31 +13,93 @@ cfg = ConfigProvider.get()
13
 
14
  _placeholder = re.compile(r"\{\{\s*([^\}]+?)\s*\}\}")
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def _render(obj: Any, session: Session, api_name: str) -> Any:
17
  """Render template with session variables and tokens"""
18
- def replacer(match):
19
- key = match.group(1)
20
- if key.startswith("variables."):
21
- var_name = key.split(".", 1)[1]
22
- return str(session.variables.get(var_name, ""))
23
- if key.startswith("auth_tokens."):
24
- parts = key.split(".")
25
- if len(parts) >= 3:
26
- token_api = parts[1]
27
- token_field = parts[2]
28
- token_data = session.auth_tokens.get(token_api, {})
29
- return str(token_data.get(token_field, ""))
30
- if key.startswith("config."):
31
- attr_name = key.split(".", 1)[1]
32
- return str(getattr(cfg.global_config, attr_name, ""))
33
- return match.group(0)
34
-
35
  if isinstance(obj, str):
 
 
 
 
 
36
  return _placeholder.sub(replacer, obj)
37
- if isinstance(obj, dict):
 
38
  return {k: _render(v, session, api_name) for k, v in obj.items()}
39
- if isinstance(obj, list):
 
40
  return [_render(v, session, api_name) for v in obj]
 
41
  return obj
42
 
43
  def _fetch_token(api: APIConfig, session: Session) -> None:
@@ -48,7 +110,8 @@ def _fetch_token(api: APIConfig, session: Session) -> None:
48
  log(f"πŸ”‘ Fetching token for {api.name}")
49
 
50
  try:
51
- body = _render(api.auth.token_request_body, session, api.name)
 
52
  headers = {"Content-Type": "application/json"}
53
 
54
  response = requests.post(
@@ -93,7 +156,7 @@ def _refresh_token(api: APIConfig, session: Session) -> bool:
93
  log(f"πŸ”„ Refreshing token for {api.name}")
94
 
95
  try:
96
- body = _render(api.auth.token_refresh_body or {}, session, api.name)
97
  body["refresh_token"] = token_info["refresh_token"]
98
 
99
  response = requests.post(
@@ -149,69 +212,14 @@ def _ensure_token(api: APIConfig, session: Session) -> None:
149
  # Refresh failed, get new token
150
  _fetch_token(api, session)
151
 
152
- def _render_json_preserving_types(obj: Any, session: Session, api_name: str) -> Any:
153
- """Render JSON object while preserving variable types"""
154
- if isinstance(obj, str):
155
- # String iΓ§inde template var mΔ± kontrol et
156
- if '{{' in obj and '}}' in obj:
157
- # Tam template mi kontrol et (ârn: "{{variables.count}}" değil de {{variables.count}})
158
- template_match = _placeholder.match(obj.strip())
159
- if template_match and template_match.group(0) == obj.strip():
160
- # Bu tam bir template, tipi koru
161
- key = template_match.group(1)
162
- if key.startswith("variables."):
163
- var_name = key.split(".", 1)[1]
164
- value = session.variables.get(var_name)
165
-
166
- # Değer tipini koru
167
- if value is None:
168
- return None
169
- elif isinstance(value, bool):
170
- return value
171
- elif isinstance(value, (int, float)):
172
- return value
173
- elif isinstance(value, str):
174
- # Date string'leri olduğu gibi bırak
175
- return value
176
- else:
177
- return str(value)
178
- else:
179
- # String iΓ§inde template var, string olarak render et
180
- return _render(obj, session, api_name)
181
- else:
182
- # Template yok, olduğu gibi dân
183
- return obj
184
- elif isinstance(obj, dict):
185
- return {k: _render_json_preserving_types(v, session, api_name) for k, v in obj.items()}
186
- elif isinstance(obj, list):
187
- return [_render_json_preserving_types(v, session, api_name) for v in obj]
188
- else:
189
- # Diğer tipler (int, float, bool, None) olduğu gibi
190
- return obj
191
-
192
  def call_api(api: APIConfig, session: Session) -> requests.Response:
193
  """Execute API call with automatic token management"""
194
  # Ensure valid token
195
  _ensure_token(api, session)
196
 
197
- # Prepare headers (bunlar her zaman string)
198
- headers = _render(api.headers, session, api.name)
199
-
200
- # Body template'i ΓΆzel olarak handle et
201
- body = api.body_template
202
-
203
- # Eğer body_template bir dict ise (JSON object)
204
- if isinstance(body, dict):
205
- # Doğrudan render et
206
- body = _render_json_preserving_types(body, session, api.name)
207
- elif isinstance(body, str):
208
- # String ise, JSON olup olmadığını kontrol et
209
- try:
210
- parsed_body = json.loads(body)
211
- body = _render_json_preserving_types(parsed_body, session, api.name)
212
- except:
213
- # JSON değilse, normal string render
214
- body = _render(body, session, api.name)
215
 
216
  # Handle proxy
217
  proxies = None
@@ -247,6 +255,7 @@ def call_api(api: APIConfig, session: Session) -> requests.Response:
247
  for attempt in range(retry_count + 1):
248
  try:
249
  log(f"🌐 API call: {api.name} {api.method} {api.url} (attempt {attempt + 1}/{retry_count + 1})")
 
250
 
251
  response = requests.request(**request_params)
252
 
@@ -261,10 +270,9 @@ def call_api(api: APIConfig, session: Session) -> requests.Response:
261
  response.raise_for_status()
262
  log(f"βœ… API call successful: {api.name} ({response.status_code})")
263
 
264
- # Response mapping işlemi - başarılı her response için
265
  if response.status_code in (200, 201, 202, 204) and hasattr(api, 'response_mappings') and api.response_mappings:
266
  try:
267
- # 204 No Content durumunda JSON parse etmeye çalışma
268
  if response.status_code != 204 and response.content:
269
  response_json = response.json()
270
 
 
4
 
5
  from __future__ import annotations
6
  import json, re, time, requests
7
+ from typing import Any, Dict, Optional, Union
8
  from utils import log
9
  from config_provider import ConfigProvider, APIConfig
10
  from session import Session
 
13
 
14
  _placeholder = re.compile(r"\{\{\s*([^\}]+?)\s*\}\}")
15
 
16
+ def _get_variable_value(session: Session, var_path: str) -> Any:
17
+ """Get variable value with proper type from session"""
18
+ if var_path.startswith("variables."):
19
+ var_name = var_path.split(".", 1)[1]
20
+ return session.variables.get(var_name)
21
+ elif var_path.startswith("auth_tokens."):
22
+ parts = var_path.split(".")
23
+ if len(parts) >= 3:
24
+ token_api = parts[1]
25
+ token_field = parts[2]
26
+ token_data = session.auth_tokens.get(token_api, {})
27
+ return token_data.get(token_field)
28
+ elif var_path.startswith("config."):
29
+ attr_name = var_path.split(".", 1)[1]
30
+ return getattr(cfg.global_config, attr_name, None)
31
+ return None
32
+
33
+ def _render_value(value: Any) -> Union[str, int, float, bool, None]:
34
+ """Convert value to appropriate JSON type"""
35
+ if value is None:
36
+ return None
37
+ elif isinstance(value, bool):
38
+ return value
39
+ elif isinstance(value, (int, float)):
40
+ return value
41
+ elif isinstance(value, str):
42
+ # Check if it's a number string
43
+ if value.isdigit():
44
+ return int(value)
45
+ try:
46
+ return float(value)
47
+ except ValueError:
48
+ pass
49
+ # Check if it's a boolean string
50
+ if value.lower() in ('true', 'false'):
51
+ return value.lower() == 'true'
52
+ # Return as string
53
+ return value
54
+ else:
55
+ return str(value)
56
+
57
+ def _render_json(obj: Any, session: Session, api_name: str) -> Any:
58
+ """Render JSON preserving types"""
59
+ if isinstance(obj, str):
60
+ # Check if entire string is a template
61
+ template_match = _placeholder.fullmatch(obj.strip())
62
+ if template_match:
63
+ # This is a pure template like {{variables.pnr}}
64
+ var_path = template_match.group(1).strip()
65
+ value = _get_variable_value(session, var_path)
66
+ return _render_value(value)
67
+ else:
68
+ # String with embedded templates or regular string
69
+ def replacer(match):
70
+ var_path = match.group(1).strip()
71
+ value = _get_variable_value(session, var_path)
72
+ return str(value) if value is not None else ""
73
+
74
+ return _placeholder.sub(replacer, obj)
75
+
76
+ elif isinstance(obj, dict):
77
+ return {k: _render_json(v, session, api_name) for k, v in obj.items()}
78
+
79
+ elif isinstance(obj, list):
80
+ return [_render_json(v, session, api_name) for v in obj]
81
+
82
+ else:
83
+ # Return as-is for numbers, booleans, None
84
+ return obj
85
+
86
  def _render(obj: Any, session: Session, api_name: str) -> Any:
87
  """Render template with session variables and tokens"""
88
+ # For headers and other string-only contexts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  if isinstance(obj, str):
90
+ def replacer(match):
91
+ var_path = match.group(1).strip()
92
+ value = _get_variable_value(session, var_path)
93
+ return str(value) if value is not None else ""
94
+
95
  return _placeholder.sub(replacer, obj)
96
+
97
+ elif isinstance(obj, dict):
98
  return {k: _render(v, session, api_name) for k, v in obj.items()}
99
+
100
+ elif isinstance(obj, list):
101
  return [_render(v, session, api_name) for v in obj]
102
+
103
  return obj
104
 
105
  def _fetch_token(api: APIConfig, session: Session) -> None:
 
110
  log(f"πŸ”‘ Fetching token for {api.name}")
111
 
112
  try:
113
+ # Use _render_json for body to preserve types
114
+ body = _render_json(api.auth.token_request_body, session, api.name)
115
  headers = {"Content-Type": "application/json"}
116
 
117
  response = requests.post(
 
156
  log(f"πŸ”„ Refreshing token for {api.name}")
157
 
158
  try:
159
+ body = _render_json(api.auth.token_refresh_body or {}, session, api.name)
160
  body["refresh_token"] = token_info["refresh_token"]
161
 
162
  response = requests.post(
 
212
  # Refresh failed, get new token
213
  _fetch_token(api, session)
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  def call_api(api: APIConfig, session: Session) -> requests.Response:
216
  """Execute API call with automatic token management"""
217
  # Ensure valid token
218
  _ensure_token(api, session)
219
 
220
+ # Prepare request
221
+ headers = _render(api.headers, session, api.name) # Headers are always strings
222
+ body = _render_json(api.body_template, session, api.name) # Body preserves types
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  # Handle proxy
225
  proxies = None
 
255
  for attempt in range(retry_count + 1):
256
  try:
257
  log(f"🌐 API call: {api.name} {api.method} {api.url} (attempt {attempt + 1}/{retry_count + 1})")
258
+ log(f"πŸ“‹ Request body: {json.dumps(body, ensure_ascii=False)}")
259
 
260
  response = requests.request(**request_params)
261
 
 
270
  response.raise_for_status()
271
  log(f"βœ… API call successful: {api.name} ({response.status_code})")
272
 
273
+ # Response mapping işlemi
274
  if response.status_code in (200, 201, 202, 204) and hasattr(api, 'response_mappings') and api.response_mappings:
275
  try:
 
276
  if response.status_code != 204 and response.content:
277
  response_json = response.json()
278