Spaces:
Sleeping
Sleeping
jayanth922
commited on
Commit
·
1f4fd74
1
Parent(s):
5332e69
feat: minimal Auth; remove Profile Switcher & Spec Health panels
Browse files- openapi_loader.py +18 -202
openapi_loader.py
CHANGED
|
@@ -1,40 +1,23 @@
|
|
| 1 |
# openapi_loader.py
|
| 2 |
from __future__ import annotations
|
| 3 |
-
import json, os, re
|
| 4 |
-
from typing import Any, Dict, Tuple, Optional
|
| 5 |
from urllib.parse import urlparse
|
| 6 |
import requests
|
| 7 |
import yaml
|
| 8 |
import gradio as gr
|
| 9 |
-
import inspect, keyword, base64
|
| 10 |
from openapi_spec_validator import validate_spec
|
| 11 |
-
import sys, yaml
|
| 12 |
-
|
| 13 |
-
|
| 14 |
|
| 15 |
Json = Dict[str, Any]
|
| 16 |
_VAR_RE = re.compile(r"\{([^}]+)\}")
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
import keyword
|
| 20 |
-
import re
|
| 21 |
|
| 22 |
_PY_IDENT = re.compile(r"^[A-Za-z_]\w*$")
|
| 23 |
|
| 24 |
def _is_valid_identifier(name: str) -> bool:
|
| 25 |
return bool(_PY_IDENT.match(name)) and not keyword.iskeyword(name)
|
| 26 |
|
| 27 |
-
def _load_profile_from_yaml(name: str):
|
| 28 |
-
"""Load (spec, base_url, bearer) from profiles.yaml in CWD."""
|
| 29 |
-
path = os.path.join(os.getcwd(), "profiles.yaml")
|
| 30 |
-
if not os.path.exists(path):
|
| 31 |
-
raise FileNotFoundError("profiles.yaml not found in the working directory.")
|
| 32 |
-
data = yaml.safe_load(open(path, "r", encoding="utf-8")) or {}
|
| 33 |
-
if name not in data:
|
| 34 |
-
raise KeyError(f"profile '{name}' not found. Available: {list(data.keys())}")
|
| 35 |
-
prof = data[name] or {}
|
| 36 |
-
return (prof.get("spec"), prof.get("base_url"), prof.get("bearer") or None), list(data.keys())
|
| 37 |
-
|
| 38 |
|
| 39 |
def sanitize_header_params(spec: Json) -> Tuple[Json, list]:
|
| 40 |
"""
|
|
@@ -174,18 +157,6 @@ def best_default_security(spec: Json) -> Optional[str]:
|
|
| 174 |
return next(iter(schemes.keys())) if schemes else None
|
| 175 |
|
| 176 |
|
| 177 |
-
def _load_profile_from_yaml(name: str) -> Tuple[Tuple[str, str, Optional[str]], List[str]]:
|
| 178 |
-
path = os.path.join(os.getcwd(), "profiles.yaml")
|
| 179 |
-
if not os.path.exists(path):
|
| 180 |
-
raise FileNotFoundError("profiles.yaml not found.")
|
| 181 |
-
data = yaml.safe_load(open(path, "r", encoding="utf-8"))
|
| 182 |
-
if name not in data:
|
| 183 |
-
raise KeyError(f"profile '{name}' not in profiles.yaml. Have: {list(data.keys())}")
|
| 184 |
-
prof = data[name] or {}
|
| 185 |
-
profile_names = list(data.keys())
|
| 186 |
-
return (prof.get("spec"), prof.get("base_url"), prof.get("bearer")), profile_names
|
| 187 |
-
|
| 188 |
-
|
| 189 |
def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
| 190 |
bearer_token: Optional[str] = None) -> gr.Blocks:
|
| 191 |
if not base_url:
|
|
@@ -224,50 +195,6 @@ def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
|
| 224 |
gr.Markdown("### AnyAPI→MCP Factory — OpenAPI Explorer")
|
| 225 |
gr.Markdown(f"**Base URL:** `{base_url}` \n**Auth:** {banner_auth}")
|
| 226 |
|
| 227 |
-
# --- Profile Switcher (reloads the process with selected profile) ---
|
| 228 |
-
with gr.Accordion("Profile switcher (restart with selected profile)", open=False):
|
| 229 |
-
profile_list = []
|
| 230 |
-
try:
|
| 231 |
-
# Load list of profile names (and ignore the selected profile here)
|
| 232 |
-
_tmp, profile_list = _load_profile_from_yaml("__dummy__") # will raise, we just want the list
|
| 233 |
-
except KeyError as _e:
|
| 234 |
-
# _e.args[0] has message with available list from loader; parse a best-effort
|
| 235 |
-
msg = str(_e)
|
| 236 |
-
start = msg.find("[")
|
| 237 |
-
end = msg.rfind("]")
|
| 238 |
-
if start != -1 and end != -1:
|
| 239 |
-
names_str = msg[start+1:end]
|
| 240 |
-
profile_list = [n.strip().strip("'\"") for n in names_str.split(",")] if names_str else []
|
| 241 |
-
except FileNotFoundError:
|
| 242 |
-
profile_list = []
|
| 243 |
-
|
| 244 |
-
prof_dd = gr.Dropdown(choices=profile_list or ["<no profiles.yaml>"],
|
| 245 |
-
value=(profile_list[0] if profile_list else "<no profiles.yaml>"),
|
| 246 |
-
label="Select profile (from profiles.yaml)")
|
| 247 |
-
reload_btn = gr.Button("Reload app with this profile")
|
| 248 |
-
reload_out = gr.Textbox(label="Reload status", interactive=False)
|
| 249 |
-
|
| 250 |
-
def _reload_with_profile(sel_name: str):
|
| 251 |
-
if not sel_name or sel_name == "<no profiles.yaml>":
|
| 252 |
-
return "❌ No profiles.yaml or no profiles found."
|
| 253 |
-
try:
|
| 254 |
-
(p_spec, p_base, p_bearer), _ = _load_profile_from_yaml(sel_name)
|
| 255 |
-
if not p_spec or not p_base:
|
| 256 |
-
return "❌ Profile missing 'spec' or 'base_url'."
|
| 257 |
-
# Build argv: python app.py --spec ... --base-url ... [--bearer ...] [--profile sel_name]
|
| 258 |
-
argv = [sys.executable, os.path.abspath(sys.argv[0]),
|
| 259 |
-
"--spec", p_spec, "--base-url", p_base]
|
| 260 |
-
if p_bearer:
|
| 261 |
-
argv += ["--bearer", p_bearer]
|
| 262 |
-
argv += ["--profile", sel_name] # keep the profile label in argv for clarity
|
| 263 |
-
# Flush response text, then execv to hard-restart the process
|
| 264 |
-
# Note: This will drop current connections; expected for a restart.
|
| 265 |
-
os.execv(sys.executable, argv)
|
| 266 |
-
except Exception as e:
|
| 267 |
-
return f"❌ {type(e).__name__}: {e}"
|
| 268 |
-
|
| 269 |
-
reload_btn.click(_reload_with_profile, inputs=[prof_dd], outputs=[reload_out])
|
| 270 |
-
|
| 271 |
# 1) Spec validator (local)
|
| 272 |
def validate_btn():
|
| 273 |
try:
|
|
@@ -289,8 +216,8 @@ def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
|
| 289 |
"oauth2": {"token": None}
|
| 290 |
})
|
| 291 |
|
| 292 |
-
# === Auth
|
| 293 |
-
with gr.Accordion("Auth
|
| 294 |
scheme_names = list(schemes.keys()) or ["<none>"]
|
| 295 |
scheme_dd = gr.Dropdown(
|
| 296 |
scheme_names,
|
|
@@ -299,8 +226,7 @@ def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
|
| 299 |
)
|
| 300 |
|
| 301 |
with gr.Row():
|
| 302 |
-
api_in = gr.Dropdown(["header", "query", "cookie"], label="apiKey.in")
|
| 303 |
-
# Make it user-editable and NEVER bind it as an output again
|
| 304 |
api_name = gr.Textbox(label="apiKey.name", interactive=True, placeholder="X-API-Key")
|
| 305 |
api_value = gr.Textbox(label="apiKey.value (secret)", type="password")
|
| 306 |
|
|
@@ -311,34 +237,22 @@ def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
|
| 311 |
basic_user = gr.Textbox(label="Basic user")
|
| 312 |
basic_pass = gr.Textbox(label="Basic password", type="password")
|
| 313 |
|
| 314 |
-
with gr.Accordion("OAuth2 (manual or client_credentials)", open=False):
|
| 315 |
-
oauth_token = gr.Textbox(label="Access token (sets Authorization: Bearer ...)", type="password")
|
| 316 |
-
token_url = gr.Textbox(label="tokenUrl (for client_credentials)")
|
| 317 |
-
client_id = gr.Textbox(label="client_id")
|
| 318 |
-
client_secret = gr.Textbox(label="client_secret", type="password")
|
| 319 |
-
scope = gr.Textbox(label="scope (space-separated)", placeholder="read write")
|
| 320 |
-
fetch_tok = gr.Button("Fetch token (client_credentials)")
|
| 321 |
-
fetch_out = gr.Textbox(label="Token fetch result", interactive=False)
|
| 322 |
-
|
| 323 |
-
# Prefill only apiKey.in from the selected scheme; leave name editable
|
| 324 |
def on_scheme_change(sel):
|
| 325 |
data = schemes.get(sel, {}) if sel and sel in schemes else {}
|
|
|
|
| 326 |
return (data.get("in") or "header")
|
| 327 |
scheme_dd.change(on_scheme_change, inputs=[scheme_dd], outputs=[api_in])
|
| 328 |
|
| 329 |
-
|
| 330 |
-
apply_btn = gr.Button("Apply auth to proxy_request")
|
| 331 |
apply_out = gr.Textbox(label="Auth status", interactive=False)
|
| 332 |
|
| 333 |
-
def apply_auth(sel, api_in_v, api_name_v, api_value_v, bearer_v, basic_u, basic_p
|
| 334 |
ctx = {
|
| 335 |
"mode": None,
|
| 336 |
"apiKey": {"in": None, "name": None, "value": None},
|
| 337 |
"bearer": {"token": None},
|
| 338 |
"basic": {"user": None, "pass": None},
|
| 339 |
-
"oauth2": {"token": None}
|
| 340 |
}
|
| 341 |
-
# Prefer defined scheme if present
|
| 342 |
if sel in schemes:
|
| 343 |
stype = (schemes[sel].get("type") or "").lower()
|
| 344 |
scheme_name = (schemes[sel].get("scheme") or "").lower()
|
|
@@ -355,60 +269,25 @@ def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
|
| 355 |
elif stype == "http" and scheme_name == "basic":
|
| 356 |
ctx["mode"] = "http_basic"
|
| 357 |
ctx["basic"] = {"user": basic_u, "pass": basic_p}
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
else:
|
| 362 |
-
# No securitySchemes: infer from what the user typed
|
| 363 |
if api_value_v and (api_in_v or api_name_v):
|
| 364 |
ctx["mode"] = "apiKey"
|
| 365 |
-
ctx["apiKey"] = {
|
| 366 |
-
"in": api_in_v or "header",
|
| 367 |
-
"name": api_name_v or "X-API-Key",
|
| 368 |
-
"value": api_value_v
|
| 369 |
-
}
|
| 370 |
elif bearer_v:
|
| 371 |
-
ctx["mode"] = "http_bearer"
|
| 372 |
-
ctx["bearer"]["token"] = bearer_v
|
| 373 |
elif basic_u or basic_p:
|
| 374 |
-
ctx["mode"] = "http_basic"
|
| 375 |
-
ctx["basic"] = {"user": basic_u, "pass": basic_p}
|
| 376 |
-
elif oauth_tok:
|
| 377 |
-
ctx["mode"] = "oauth2"
|
| 378 |
-
ctx["oauth2"]["token"] = oauth_tok
|
| 379 |
|
| 380 |
return ctx, f"Applied auth mode: {ctx['mode']}"
|
| 381 |
|
| 382 |
apply_btn.click(
|
| 383 |
fn=apply_auth,
|
| 384 |
-
inputs=[scheme_dd, api_in, api_name, api_value, bearer_inp, basic_user, basic_pass
|
| 385 |
outputs=[auth_ctx, apply_out]
|
| 386 |
)
|
| 387 |
-
|
| 388 |
-
# Client Credentials fetch helper (optional)
|
| 389 |
-
def fetch_token_cc(token_url_v, client_id_v, client_secret_v, scope_v):
|
| 390 |
-
if not token_url_v:
|
| 391 |
-
return "❌ tokenUrl required"
|
| 392 |
-
try:
|
| 393 |
-
data = {"grant_type": "client_credentials"}
|
| 394 |
-
if scope_v: data["scope"] = scope_v
|
| 395 |
-
resp = requests.post(
|
| 396 |
-
token_url_v, data=data,
|
| 397 |
-
auth=(client_id_v or "", client_secret_v or ""), timeout=30
|
| 398 |
-
)
|
| 399 |
-
resp.raise_for_status()
|
| 400 |
-
payload = resp.json()
|
| 401 |
-
tok = payload.get("access_token")
|
| 402 |
-
return f"✅ got token: {('***' if tok else '<none>')}"
|
| 403 |
-
except Exception as e:
|
| 404 |
-
return f"❌ {type(e).__name__}: {e}"
|
| 405 |
-
|
| 406 |
-
fetch_tok.click(
|
| 407 |
-
fetch_token_cc,
|
| 408 |
-
inputs=[token_url, client_id, client_secret, scope],
|
| 409 |
-
outputs=[fetch_out]
|
| 410 |
-
)
|
| 411 |
-
# === end Auth Wizard ===
|
| 412 |
|
| 413 |
# 4) Render auto-generated API UI
|
| 414 |
loaded_app.render()
|
|
@@ -476,69 +355,6 @@ def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
|
| 476 |
status_out = f"{resp.status_code} {resp.reason}"
|
| 477 |
return status_out, body_out, headers_out
|
| 478 |
|
| 479 |
-
# --- Spec Health (Modal) ---
|
| 480 |
-
with gr.Accordion("Spec Health (Modal)", open=False):
|
| 481 |
-
status_url = gr.Textbox(
|
| 482 |
-
label="Status URL (exact)",
|
| 483 |
-
placeholder="https://<your-status>.modal.run",
|
| 484 |
-
value=os.environ.get("MODAL_STATUS_URL", "")
|
| 485 |
-
)
|
| 486 |
-
download_url = gr.Textbox(
|
| 487 |
-
label="Download URL (exact)",
|
| 488 |
-
placeholder="https://<your-download>.modal.run",
|
| 489 |
-
value=os.environ.get("MODAL_DOWNLOAD_URL", "")
|
| 490 |
-
)
|
| 491 |
-
|
| 492 |
-
fetch = gr.Button("Fetch status")
|
| 493 |
-
used_status_url = gr.Textbox(label="(debug) used status URL", interactive=False)
|
| 494 |
-
status_out = gr.Textbox(label="Result", interactive=False)
|
| 495 |
-
status_json = gr.JSON(label="Status JSON")
|
| 496 |
-
|
| 497 |
-
validated_dd = gr.Dropdown(choices=[], label="Validated profile")
|
| 498 |
-
reload_validated_btn = gr.Button("Reload with validated spec")
|
| 499 |
-
reload_validated_out = gr.Textbox(label="Reload result", interactive=False)
|
| 500 |
-
|
| 501 |
-
def _fetch_status(status_u: str):
|
| 502 |
-
if not status_u:
|
| 503 |
-
return "", "❌ Provide the full Status URL", None
|
| 504 |
-
try:
|
| 505 |
-
# DO NOT append '/status' — this URL *is* the endpoint
|
| 506 |
-
r = requests.get(status_u, timeout=20, allow_redirects=True)
|
| 507 |
-
r.raise_for_status()
|
| 508 |
-
data = r.json()
|
| 509 |
-
profiles = sorted(list(data.keys()))
|
| 510 |
-
note = f"✅ {len(profiles)} profiles fetched"
|
| 511 |
-
dd = gr.update(choices=profiles, value=(profiles[0] if profiles else None))
|
| 512 |
-
return status_u, note, data, dd
|
| 513 |
-
except Exception as e:
|
| 514 |
-
return status_u, f"❌ {type(e).__name__}: {e}", None, gr.update(choices=[], value=None)
|
| 515 |
-
|
| 516 |
-
fetch.click(_fetch_status, inputs=[status_url], outputs=[used_status_url, status_out, status_json, validated_dd])
|
| 517 |
-
|
| 518 |
-
def _reload_with_validated(profile_name: str, download_u: str):
|
| 519 |
-
if not profile_name:
|
| 520 |
-
return "❌ Select a profile."
|
| 521 |
-
if not download_u:
|
| 522 |
-
return "❌ Provide the full Download URL."
|
| 523 |
-
|
| 524 |
-
# Read base_url from profiles.yaml
|
| 525 |
-
try:
|
| 526 |
-
(p_spec, p_base, p_bearer), _ = _load_profile_from_yaml(profile_name)
|
| 527 |
-
except Exception:
|
| 528 |
-
p_spec, p_base, p_bearer = None, None, None
|
| 529 |
-
if not p_base:
|
| 530 |
-
return "❌ profiles.yaml must include base_url for this profile."
|
| 531 |
-
|
| 532 |
-
# Modal download endpoint uses ?profile=...
|
| 533 |
-
spec_url = f"{download_u}{'' if '?' in download_u else ''}?profile={profile_name}"
|
| 534 |
-
|
| 535 |
-
argv = [sys.executable, os.path.abspath(sys.argv[0]), "--spec", spec_url, "--base-url", p_base, "--profile", profile_name]
|
| 536 |
-
if p_bearer:
|
| 537 |
-
argv += ["--bearer", p_bearer]
|
| 538 |
-
os.execv(sys.executable, argv)
|
| 539 |
-
|
| 540 |
-
reload_validated_btn.click(_reload_with_validated, inputs=[validated_dd, download_url], outputs=[reload_validated_out])
|
| 541 |
-
|
| 542 |
with gr.Accordion("Advanced request (API-Key / Basic / custom headers)", open=False):
|
| 543 |
with gr.Row():
|
| 544 |
method = gr.Dropdown(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"], value="GET", label="Method")
|
|
|
|
| 1 |
# openapi_loader.py
|
| 2 |
from __future__ import annotations
|
| 3 |
+
import json, os, re
|
| 4 |
+
from typing import Any, Dict, Tuple, Optional
|
| 5 |
from urllib.parse import urlparse
|
| 6 |
import requests
|
| 7 |
import yaml
|
| 8 |
import gradio as gr
|
| 9 |
+
import inspect, keyword, base64
|
| 10 |
from openapi_spec_validator import validate_spec
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
Json = Dict[str, Any]
|
| 13 |
_VAR_RE = re.compile(r"\{([^}]+)\}")
|
| 14 |
+
_PY_IDENT = re.compile(r"^[A-Za-z_]\w*$")
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
_PY_IDENT = re.compile(r"^[A-Za-z_]\w*$")
|
| 17 |
|
| 18 |
def _is_valid_identifier(name: str) -> bool:
|
| 19 |
return bool(_PY_IDENT.match(name)) and not keyword.iskeyword(name)
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
def sanitize_header_params(spec: Json) -> Tuple[Json, list]:
|
| 23 |
"""
|
|
|
|
| 157 |
return next(iter(schemes.keys())) if schemes else None
|
| 158 |
|
| 159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
def build_gradio_app_from_spec(spec: Json, base_url: Optional[str] = None,
|
| 161 |
bearer_token: Optional[str] = None) -> gr.Blocks:
|
| 162 |
if not base_url:
|
|
|
|
| 195 |
gr.Markdown("### AnyAPI→MCP Factory — OpenAPI Explorer")
|
| 196 |
gr.Markdown(f"**Base URL:** `{base_url}` \n**Auth:** {banner_auth}")
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
# 1) Spec validator (local)
|
| 199 |
def validate_btn():
|
| 200 |
try:
|
|
|
|
| 216 |
"oauth2": {"token": None}
|
| 217 |
})
|
| 218 |
|
| 219 |
+
# === Auth (minimal) ===
|
| 220 |
+
with gr.Accordion("Auth", open=True):
|
| 221 |
scheme_names = list(schemes.keys()) or ["<none>"]
|
| 222 |
scheme_dd = gr.Dropdown(
|
| 223 |
scheme_names,
|
|
|
|
| 226 |
)
|
| 227 |
|
| 228 |
with gr.Row():
|
| 229 |
+
api_in = gr.Dropdown(["header", "query", "cookie"], value="header", label="apiKey.in")
|
|
|
|
| 230 |
api_name = gr.Textbox(label="apiKey.name", interactive=True, placeholder="X-API-Key")
|
| 231 |
api_value = gr.Textbox(label="apiKey.value (secret)", type="password")
|
| 232 |
|
|
|
|
| 237 |
basic_user = gr.Textbox(label="Basic user")
|
| 238 |
basic_pass = gr.Textbox(label="Basic password", type="password")
|
| 239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
def on_scheme_change(sel):
|
| 241 |
data = schemes.get(sel, {}) if sel and sel in schemes else {}
|
| 242 |
+
# Only prefill apiKey.in; leave name editable
|
| 243 |
return (data.get("in") or "header")
|
| 244 |
scheme_dd.change(on_scheme_change, inputs=[scheme_dd], outputs=[api_in])
|
| 245 |
|
| 246 |
+
apply_btn = gr.Button("Apply auth")
|
|
|
|
| 247 |
apply_out = gr.Textbox(label="Auth status", interactive=False)
|
| 248 |
|
| 249 |
+
def apply_auth(sel, api_in_v, api_name_v, api_value_v, bearer_v, basic_u, basic_p):
|
| 250 |
ctx = {
|
| 251 |
"mode": None,
|
| 252 |
"apiKey": {"in": None, "name": None, "value": None},
|
| 253 |
"bearer": {"token": None},
|
| 254 |
"basic": {"user": None, "pass": None},
|
|
|
|
| 255 |
}
|
|
|
|
| 256 |
if sel in schemes:
|
| 257 |
stype = (schemes[sel].get("type") or "").lower()
|
| 258 |
scheme_name = (schemes[sel].get("scheme") or "").lower()
|
|
|
|
| 269 |
elif stype == "http" and scheme_name == "basic":
|
| 270 |
ctx["mode"] = "http_basic"
|
| 271 |
ctx["basic"] = {"user": basic_u, "pass": basic_p}
|
| 272 |
+
|
| 273 |
+
# Fallbacks if the spec doesn't define a scheme
|
| 274 |
+
if ctx["mode"] is None:
|
|
|
|
|
|
|
| 275 |
if api_value_v and (api_in_v or api_name_v):
|
| 276 |
ctx["mode"] = "apiKey"
|
| 277 |
+
ctx["apiKey"] = {"in": api_in_v or "header", "name": api_name_v or "X-API-Key", "value": api_value_v}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
elif bearer_v:
|
| 279 |
+
ctx["mode"] = "http_bearer"; ctx["bearer"]["token"] = bearer_v
|
|
|
|
| 280 |
elif basic_u or basic_p:
|
| 281 |
+
ctx["mode"] = "http_basic"; ctx["basic"] = {"user": basic_u, "pass": basic_p}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
return ctx, f"Applied auth mode: {ctx['mode']}"
|
| 284 |
|
| 285 |
apply_btn.click(
|
| 286 |
fn=apply_auth,
|
| 287 |
+
inputs=[scheme_dd, api_in, api_name, api_value, bearer_inp, basic_user, basic_pass],
|
| 288 |
outputs=[auth_ctx, apply_out]
|
| 289 |
)
|
| 290 |
+
# === end Auth ===
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
|
| 292 |
# 4) Render auto-generated API UI
|
| 293 |
loaded_app.render()
|
|
|
|
| 355 |
status_out = f"{resp.status_code} {resp.reason}"
|
| 356 |
return status_out, body_out, headers_out
|
| 357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
with gr.Accordion("Advanced request (API-Key / Basic / custom headers)", open=False):
|
| 359 |
with gr.Row():
|
| 360 |
method = gr.Dropdown(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"], value="GET", label="Method")
|