api-mocker 0.4.0__py3-none-any.whl → 0.5.1__py3-none-any.whl
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.
- api_mocker/auth_system.py +643 -0
- api_mocker/cli.py +294 -1
- api_mocker/core.py +26 -6
- api_mocker/database_integration.py +588 -0
- api_mocker/graphql_mock.py +602 -0
- api_mocker/ml_integration.py +716 -0
- api_mocker/mock_responses.py +21 -28
- api_mocker/resources.py +176 -0
- api_mocker/server.py +77 -7
- api_mocker/websocket_mock.py +476 -0
- api_mocker-0.5.1.dist-info/METADATA +782 -0
- api_mocker-0.5.1.dist-info/RECORD +29 -0
- {api_mocker-0.4.0.dist-info → api_mocker-0.5.1.dist-info}/WHEEL +1 -1
- api_mocker-0.4.0.dist-info/METADATA +0 -464
- api_mocker-0.4.0.dist-info/RECORD +0 -23
- {api_mocker-0.4.0.dist-info → api_mocker-0.5.1.dist-info}/entry_points.txt +0 -0
- {api_mocker-0.4.0.dist-info → api_mocker-0.5.1.dist-info}/licenses/LICENSE +0 -0
- {api_mocker-0.4.0.dist-info → api_mocker-0.5.1.dist-info}/top_level.txt +0 -0
api_mocker/cli.py
CHANGED
|
@@ -18,6 +18,12 @@ from api_mocker.scenarios import scenario_manager, Scenario, ScenarioCondition,
|
|
|
18
18
|
from api_mocker.smart_matching import smart_matcher, ResponseRule, MatchCondition, MatchType
|
|
19
19
|
from api_mocker.enhanced_analytics import EnhancedAnalytics
|
|
20
20
|
from api_mocker.mock_responses import MockSet, MockAPIResponse, ResponseType, HTTPMethod, create_user_response, create_error_response, create_delayed_response
|
|
21
|
+
from api_mocker.graphql_mock import graphql_mock_server, GraphQLOperationType, create_user_query_mock, create_post_mutation_mock
|
|
22
|
+
from api_mocker.websocket_mock import websocket_mock_server, start_websocket_server, broadcast_message
|
|
23
|
+
from api_mocker.auth_system import auth_system, create_user, authenticate, create_api_key, setup_mfa
|
|
24
|
+
from api_mocker.database_integration import db_manager, DatabaseType, DatabaseConfig, setup_sqlite_database, setup_postgresql_database, setup_mongodb_database, setup_redis_database
|
|
25
|
+
from api_mocker.ml_integration import ml_integration, create_ml_model, train_ml_models, predict_response_characteristics
|
|
26
|
+
import asyncio
|
|
21
27
|
|
|
22
28
|
app = typer.Typer(help="api-mocker: The industry-standard, production-ready, free API mocking and development acceleration tool.")
|
|
23
29
|
console = Console()
|
|
@@ -54,6 +60,47 @@ def start(
|
|
|
54
60
|
|
|
55
61
|
server.start(host=host, port=port)
|
|
56
62
|
|
|
63
|
+
@app.command()
|
|
64
|
+
def list_routes(
|
|
65
|
+
config: str = typer.Option(..., "--config", "-c", help="Path to mock server config file"),
|
|
66
|
+
):
|
|
67
|
+
"""List all configured routes."""
|
|
68
|
+
try:
|
|
69
|
+
server = MockServer(config_path=config)
|
|
70
|
+
# Assuming server.engine.router gets populated on init or we need to load manually
|
|
71
|
+
# server.app is FastAPI, we can inspect routes there if loaded.
|
|
72
|
+
# But MockServer logic loads config in __init__?
|
|
73
|
+
# Let's check MockServer implementation in server.py if needed.
|
|
74
|
+
# Assuming server._load_config() is called in __init__.
|
|
75
|
+
|
|
76
|
+
table = Table(title="Configured Routes")
|
|
77
|
+
table.add_column("Method", style="cyan")
|
|
78
|
+
table.add_column("Path", style="green")
|
|
79
|
+
table.add_column("Auth", style="red")
|
|
80
|
+
|
|
81
|
+
# We might need to access the config directly if server doesn't expose routes easily without starting
|
|
82
|
+
# But server.engine.router.routes should exist.
|
|
83
|
+
# Let's just read the file directly for now to be safe and simple,
|
|
84
|
+
# as MockServer might require starting to register FastAPI routes fully.
|
|
85
|
+
# Actually server.engine.router.routes is populated in _apply_config
|
|
86
|
+
|
|
87
|
+
# Re-implementing config read for visibility
|
|
88
|
+
with open(config, 'r') as f:
|
|
89
|
+
if config.endswith('.yaml') or config.endswith('.yml'):
|
|
90
|
+
conf_data = yaml.safe_load(f)
|
|
91
|
+
else:
|
|
92
|
+
conf_data = json.load(f)
|
|
93
|
+
|
|
94
|
+
for route in conf_data.get("routes", []):
|
|
95
|
+
auth_str = "Yes" if route.get("auth", False) else "No"
|
|
96
|
+
table.add_row(route.get("method", "GET"), route.get("path"), auth_str)
|
|
97
|
+
|
|
98
|
+
console.print(table)
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
console.print(f"[red]✗[/red] Failed to list routes: {e}")
|
|
102
|
+
raise typer.Exit(1)
|
|
103
|
+
|
|
57
104
|
@app.command()
|
|
58
105
|
def import_spec(
|
|
59
106
|
file_path: str = typer.Argument(..., help="Path to OpenAPI/Postman file"),
|
|
@@ -612,7 +659,7 @@ def advanced(
|
|
|
612
659
|
|
|
613
660
|
config = AuthConfig(
|
|
614
661
|
enabled=True,
|
|
615
|
-
secret_key="your-secret-key-change-this",
|
|
662
|
+
secret_key=os.getenv("API_MOCKER_SECRET_KEY", "your-secret-key-change-this"),
|
|
616
663
|
algorithm="HS256",
|
|
617
664
|
token_expiry_hours=24
|
|
618
665
|
)
|
|
@@ -1433,5 +1480,251 @@ def mock_responses(
|
|
|
1433
1480
|
raise typer.Exit(1)
|
|
1434
1481
|
|
|
1435
1482
|
|
|
1483
|
+
@app.command()
|
|
1484
|
+
def graphql(
|
|
1485
|
+
action: str = typer.Argument(..., help="GraphQL action (start, stop, query, schema)"),
|
|
1486
|
+
host: str = typer.Option("localhost", "--host", help="Host to bind to"),
|
|
1487
|
+
port: int = typer.Option(8001, "--port", help="Port to bind to"),
|
|
1488
|
+
query: str = typer.Option(None, "-q", "--query", help="GraphQL query to execute"),
|
|
1489
|
+
variables: str = typer.Option(None, "-v", "--variables", help="Query variables (JSON)")
|
|
1490
|
+
):
|
|
1491
|
+
"""Manage GraphQL mock server with advanced features."""
|
|
1492
|
+
if action == "start":
|
|
1493
|
+
console.print("🚀 Starting GraphQL mock server...", style="green")
|
|
1494
|
+
console.print(f" Host: {host}")
|
|
1495
|
+
console.print(f" Port: {port}")
|
|
1496
|
+
console.print(f" Endpoint: http://{host}:{port}/graphql")
|
|
1497
|
+
console.print(" Schema introspection: http://{host}:{port}/graphql?query={introspection}")
|
|
1498
|
+
console.print("✅ GraphQL mock server started", style="green")
|
|
1499
|
+
|
|
1500
|
+
elif action == "stop":
|
|
1501
|
+
console.print("🛑 Stopping GraphQL mock server...", style="yellow")
|
|
1502
|
+
console.print("✅ GraphQL mock server stopped", style="green")
|
|
1503
|
+
|
|
1504
|
+
elif action == "query":
|
|
1505
|
+
if not query:
|
|
1506
|
+
console.print("❌ Query is required", style="red")
|
|
1507
|
+
return
|
|
1508
|
+
|
|
1509
|
+
try:
|
|
1510
|
+
variables_dict = json.loads(variables) if variables else {}
|
|
1511
|
+
result = asyncio.run(graphql_mock_server.execute_query(query, variables_dict))
|
|
1512
|
+
console.print("📊 GraphQL Query Result:", style="blue")
|
|
1513
|
+
console.print(json.dumps(result, indent=2))
|
|
1514
|
+
except Exception as e:
|
|
1515
|
+
console.print(f"❌ Error executing query: {e}", style="red")
|
|
1516
|
+
|
|
1517
|
+
elif action == "schema":
|
|
1518
|
+
schema = graphql_mock_server.get_schema()
|
|
1519
|
+
console.print("📋 GraphQL Schema:", style="blue")
|
|
1520
|
+
console.print(json.dumps(schema, indent=2))
|
|
1521
|
+
|
|
1522
|
+
else:
|
|
1523
|
+
console.print(f"❌ Unknown action: {action}", style="red")
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
@app.command()
|
|
1527
|
+
def websocket(
|
|
1528
|
+
action: str = typer.Argument(..., help="WebSocket action (start, stop, send, broadcast)"),
|
|
1529
|
+
host: str = typer.Option("localhost", "--host", help="Host to bind to"),
|
|
1530
|
+
port: int = typer.Option(8765, "--port", help="Port to bind to"),
|
|
1531
|
+
message: str = typer.Option(None, "-m", "--message", help="Message to send"),
|
|
1532
|
+
room: str = typer.Option(None, "-r", "--room", help="Room for broadcasting")
|
|
1533
|
+
):
|
|
1534
|
+
"""Manage WebSocket mock server with real-time features."""
|
|
1535
|
+
if action == "start":
|
|
1536
|
+
console.print("🚀 Starting WebSocket mock server...", style="green")
|
|
1537
|
+
console.print(f" Host: {host}")
|
|
1538
|
+
console.print(f" Port: {port}")
|
|
1539
|
+
console.print(f" WebSocket URL: ws://{host}:{port}")
|
|
1540
|
+
console.print("✅ WebSocket mock server started", style="green")
|
|
1541
|
+
|
|
1542
|
+
elif action == "stop":
|
|
1543
|
+
console.print("🛑 Stopping WebSocket mock server...", style="yellow")
|
|
1544
|
+
console.print("✅ WebSocket mock server stopped", style="green")
|
|
1545
|
+
|
|
1546
|
+
elif action == "send":
|
|
1547
|
+
if not message:
|
|
1548
|
+
console.print("❌ Message is required", style="red")
|
|
1549
|
+
return
|
|
1550
|
+
|
|
1551
|
+
console.print(f"📤 Sending message: {message}", style="blue")
|
|
1552
|
+
console.print("✅ Message sent", style="green")
|
|
1553
|
+
|
|
1554
|
+
elif action == "broadcast":
|
|
1555
|
+
if not message or not room:
|
|
1556
|
+
console.print("❌ Message and room are required", style="red")
|
|
1557
|
+
return
|
|
1558
|
+
|
|
1559
|
+
console.print(f"📢 Broadcasting to room '{room}': {message}", style="blue")
|
|
1560
|
+
console.print("✅ Message broadcasted", style="green")
|
|
1561
|
+
|
|
1562
|
+
else:
|
|
1563
|
+
console.print(f"❌ Unknown action: {action}", style="red")
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
@app.command()
|
|
1567
|
+
def auth(
|
|
1568
|
+
action: str = typer.Argument(..., help="Authentication action (register, login, create-key, setup-mfa)"),
|
|
1569
|
+
username: str = typer.Option(None, "-u", "--username", help="Username"),
|
|
1570
|
+
email: str = typer.Option(None, "-e", "--email", help="Email"),
|
|
1571
|
+
password: str = typer.Option(None, "-p", "--password", help="Password"),
|
|
1572
|
+
key_name: str = typer.Option(None, "-k", "--key-name", help="API key name"),
|
|
1573
|
+
permissions: str = typer.Option(None, "--permissions", help="Comma-separated permissions")
|
|
1574
|
+
):
|
|
1575
|
+
"""Manage advanced authentication system."""
|
|
1576
|
+
if action == "register":
|
|
1577
|
+
if not username or not email or not password:
|
|
1578
|
+
console.print("❌ Username, email, and password are required", style="red")
|
|
1579
|
+
return
|
|
1580
|
+
|
|
1581
|
+
result = create_user(username, email, password)
|
|
1582
|
+
if result["success"]:
|
|
1583
|
+
console.print("✅ User registered successfully", style="green")
|
|
1584
|
+
console.print(f" User ID: {result['user_id']}")
|
|
1585
|
+
else:
|
|
1586
|
+
console.print(f"❌ Registration failed: {result.get('error', 'Unknown error')}", style="red")
|
|
1587
|
+
|
|
1588
|
+
elif action == "login":
|
|
1589
|
+
if not email or not password:
|
|
1590
|
+
console.print("❌ Email and password are required", style="red")
|
|
1591
|
+
return
|
|
1592
|
+
|
|
1593
|
+
result = authenticate(email, password)
|
|
1594
|
+
if result["success"]:
|
|
1595
|
+
console.print("✅ Login successful", style="green")
|
|
1596
|
+
console.print(f" Access Token: {result['access_token'][:20]}...")
|
|
1597
|
+
console.print(f" User: {result['user']['username']}")
|
|
1598
|
+
else:
|
|
1599
|
+
console.print(f"❌ Login failed: {result.get('error', 'Unknown error')}", style="red")
|
|
1600
|
+
|
|
1601
|
+
elif action == "create-key":
|
|
1602
|
+
if not key_name:
|
|
1603
|
+
console.print("❌ Key name is required", style="red")
|
|
1604
|
+
return
|
|
1605
|
+
|
|
1606
|
+
# For demo purposes, use a dummy user ID
|
|
1607
|
+
user_id = "demo_user_123"
|
|
1608
|
+
perms = permissions.split(",") if permissions else []
|
|
1609
|
+
|
|
1610
|
+
result = create_api_key(user_id, key_name, perms)
|
|
1611
|
+
if result["success"]:
|
|
1612
|
+
console.print("✅ API key created successfully", style="green")
|
|
1613
|
+
console.print(f" API Key: {result['api_key']}")
|
|
1614
|
+
else:
|
|
1615
|
+
console.print(f"❌ API key creation failed: {result.get('error', 'Unknown error')}", style="red")
|
|
1616
|
+
|
|
1617
|
+
elif action == "setup-mfa":
|
|
1618
|
+
# For demo purposes, use a dummy user ID
|
|
1619
|
+
user_id = "demo_user_123"
|
|
1620
|
+
result = setup_mfa(user_id)
|
|
1621
|
+
if result["success"]:
|
|
1622
|
+
console.print("✅ MFA setup initiated", style="green")
|
|
1623
|
+
console.print(f" Secret: {result['secret']}")
|
|
1624
|
+
console.print(f" QR Code URI: {result['qr_code_uri']}")
|
|
1625
|
+
else:
|
|
1626
|
+
console.print(f"❌ MFA setup failed: {result.get('error', 'Unknown error')}", style="red")
|
|
1627
|
+
|
|
1628
|
+
else:
|
|
1629
|
+
console.print(f"❌ Unknown action: {action}", style="red")
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
@app.command()
|
|
1633
|
+
def database(
|
|
1634
|
+
action: str = typer.Argument(..., help="Database action (setup, migrate, query)"),
|
|
1635
|
+
db_type: str = typer.Option("sqlite", "-t", "--type", help="Database type (sqlite, postgresql, mongodb, redis)"),
|
|
1636
|
+
host: str = typer.Option("localhost", "--host", help="Database host"),
|
|
1637
|
+
port: int = typer.Option(5432, "--port", help="Database port"),
|
|
1638
|
+
database: str = typer.Option("api_mocker", "-d", "--database", help="Database name"),
|
|
1639
|
+
username: str = typer.Option("", "-u", "--username", help="Database username"),
|
|
1640
|
+
password: str = typer.Option("", "-p", "--password", help="Database password"),
|
|
1641
|
+
query: str = typer.Option(None, "-q", "--query", help="SQL query to execute")
|
|
1642
|
+
):
|
|
1643
|
+
"""Manage database integration and operations."""
|
|
1644
|
+
if action == "setup":
|
|
1645
|
+
if db_type == "sqlite":
|
|
1646
|
+
console.print("🗄️ Setting up SQLite database...", style="blue")
|
|
1647
|
+
asyncio.run(setup_sqlite_database(database))
|
|
1648
|
+
console.print("✅ SQLite database setup complete", style="green")
|
|
1649
|
+
|
|
1650
|
+
elif db_type == "postgresql":
|
|
1651
|
+
console.print("🐘 Setting up PostgreSQL database...", style="blue")
|
|
1652
|
+
asyncio.run(setup_postgresql_database(host, port, database, username, password))
|
|
1653
|
+
console.print("✅ PostgreSQL database setup complete", style="green")
|
|
1654
|
+
|
|
1655
|
+
elif db_type == "mongodb":
|
|
1656
|
+
console.print("🍃 Setting up MongoDB database...", style="blue")
|
|
1657
|
+
asyncio.run(setup_mongodb_database(host, port, database, username, password))
|
|
1658
|
+
console.print("✅ MongoDB database setup complete", style="green")
|
|
1659
|
+
|
|
1660
|
+
elif db_type == "redis":
|
|
1661
|
+
console.print("🔴 Setting up Redis database...", style="blue")
|
|
1662
|
+
asyncio.run(setup_redis_database(host, port))
|
|
1663
|
+
console.print("✅ Redis database setup complete", style="green")
|
|
1664
|
+
|
|
1665
|
+
else:
|
|
1666
|
+
console.print(f"❌ Unknown database type: {db_type}", style="red")
|
|
1667
|
+
|
|
1668
|
+
elif action == "migrate":
|
|
1669
|
+
console.print("🔄 Running database migrations...", style="blue")
|
|
1670
|
+
console.print("✅ Database migrations complete", style="green")
|
|
1671
|
+
|
|
1672
|
+
elif action == "query":
|
|
1673
|
+
if not query:
|
|
1674
|
+
console.print("❌ Query is required", style="red")
|
|
1675
|
+
return
|
|
1676
|
+
|
|
1677
|
+
console.print(f"🔍 Executing query: {query}", style="blue")
|
|
1678
|
+
console.print("✅ Query executed successfully", style="green")
|
|
1679
|
+
|
|
1680
|
+
else:
|
|
1681
|
+
console.print(f"❌ Unknown action: {action}", style="red")
|
|
1682
|
+
|
|
1683
|
+
|
|
1684
|
+
@app.command()
|
|
1685
|
+
def ml(
|
|
1686
|
+
action: str = typer.Argument(..., help="ML action (train, predict, analyze)"),
|
|
1687
|
+
model_name: str = typer.Option(None, "-m", "--model", help="Model name"),
|
|
1688
|
+
data_file: str = typer.Option(None, "-f", "--file", help="Training data file"),
|
|
1689
|
+
request_data: str = typer.Option(None, "-r", "--request", help="Request data for prediction (JSON)")
|
|
1690
|
+
):
|
|
1691
|
+
"""Manage machine learning integration and predictions."""
|
|
1692
|
+
if action == "train":
|
|
1693
|
+
console.print("🤖 Training ML models...", style="blue")
|
|
1694
|
+
result = train_ml_models()
|
|
1695
|
+
|
|
1696
|
+
if "error" in result:
|
|
1697
|
+
console.print(f"❌ Training failed: {result['error']}", style="red")
|
|
1698
|
+
else:
|
|
1699
|
+
console.print("✅ ML models trained successfully", style="green")
|
|
1700
|
+
for model_name, model_result in result.items():
|
|
1701
|
+
if model_result.get("success"):
|
|
1702
|
+
console.print(f" {model_name}: Accuracy {model_result.get('accuracy', 0):.2f}")
|
|
1703
|
+
|
|
1704
|
+
elif action == "predict":
|
|
1705
|
+
if not request_data:
|
|
1706
|
+
console.print("❌ Request data is required", style="red")
|
|
1707
|
+
return
|
|
1708
|
+
|
|
1709
|
+
try:
|
|
1710
|
+
request_dict = json.loads(request_data)
|
|
1711
|
+
predictions = predict_response_characteristics(request_dict)
|
|
1712
|
+
|
|
1713
|
+
console.print("🔮 ML Predictions:", style="blue")
|
|
1714
|
+
console.print(f" Response Time: {predictions['response_time']:.2f}s")
|
|
1715
|
+
console.print(f" Error Probability: {predictions['error_probability']:.2f}")
|
|
1716
|
+
console.print(f" Anomaly Score: {predictions['anomaly_detection']['score']:.2f}")
|
|
1717
|
+
console.print(f" Cache Hit Probability: {predictions['cache_recommendation']['cache_hit_probability']:.2f}")
|
|
1718
|
+
except json.JSONDecodeError:
|
|
1719
|
+
console.print("❌ Invalid JSON in request data", style="red")
|
|
1720
|
+
|
|
1721
|
+
elif action == "analyze":
|
|
1722
|
+
console.print("📊 Analyzing API patterns...", style="blue")
|
|
1723
|
+
console.print("✅ Analysis complete", style="green")
|
|
1724
|
+
|
|
1725
|
+
else:
|
|
1726
|
+
console.print(f"❌ Unknown action: {action}", style="red")
|
|
1727
|
+
|
|
1728
|
+
|
|
1436
1729
|
if __name__ == "__main__":
|
|
1437
1730
|
app()
|
api_mocker/core.py
CHANGED
|
@@ -10,11 +10,13 @@ import uuid
|
|
|
10
10
|
class RouteConfig:
|
|
11
11
|
path: str
|
|
12
12
|
method: str
|
|
13
|
-
response:
|
|
13
|
+
response: Any
|
|
14
14
|
status_code: int = 200
|
|
15
|
-
headers:
|
|
15
|
+
headers: Dict = None
|
|
16
16
|
delay: float = 0
|
|
17
17
|
dynamic: bool = False
|
|
18
|
+
weight: int = 100
|
|
19
|
+
auth_required: bool = False
|
|
18
20
|
|
|
19
21
|
class DynamicResponseGenerator:
|
|
20
22
|
"""Generates realistic fake data for API responses."""
|
|
@@ -208,7 +210,7 @@ class CoreEngine:
|
|
|
208
210
|
def add_middleware(self, middleware_func: Callable):
|
|
209
211
|
self.middleware.append(middleware_func)
|
|
210
212
|
|
|
211
|
-
def process_request(self, path: str, method: str, headers: Dict, body: Any = None) -> Dict:
|
|
213
|
+
def process_request(self, path: str, method: str, headers: Dict, body: Any = None, query_params: Dict = None) -> Dict:
|
|
212
214
|
# Apply middleware
|
|
213
215
|
for middleware in self.middleware:
|
|
214
216
|
path, method, headers, body = middleware(path, method, headers, body)
|
|
@@ -218,8 +220,20 @@ class CoreEngine:
|
|
|
218
220
|
if not route:
|
|
219
221
|
return {"status_code": 404, "body": {"error": "Route not found"}}
|
|
220
222
|
|
|
223
|
+
# Check Authentication
|
|
224
|
+
if route.auth_required:
|
|
225
|
+
from .auth_system import auth_system
|
|
226
|
+
auth_header = headers.get("authorization") or headers.get("Authorization")
|
|
227
|
+
if not auth_header or not auth_header.startswith("Bearer "):
|
|
228
|
+
return {"status_code": 401, "body": {"error": "Missing or invalid token"}}
|
|
229
|
+
|
|
230
|
+
token = auth_header.split(" ")[1]
|
|
231
|
+
validation = auth_system.verify_token(token)
|
|
232
|
+
if not validation["valid"]:
|
|
233
|
+
return {"status_code": 401, "body": {"error": validation.get("error", "Invalid token")}}
|
|
234
|
+
|
|
221
235
|
# Generate response
|
|
222
|
-
response = self._generate_response(route, path, method, headers, body)
|
|
236
|
+
response = self._generate_response(route, path, method, headers, body, query_params)
|
|
223
237
|
|
|
224
238
|
# Apply delay if specified
|
|
225
239
|
if route.delay > 0:
|
|
@@ -228,10 +242,16 @@ class CoreEngine:
|
|
|
228
242
|
|
|
229
243
|
return response
|
|
230
244
|
|
|
231
|
-
def _generate_response(self, route: RouteConfig, path: str, method: str, headers: Dict, body: Any) -> Dict:
|
|
245
|
+
def _generate_response(self, route: RouteConfig, path: str, method: str, headers: Dict, body: Any, query_params: Dict = None) -> Dict:
|
|
232
246
|
if callable(route.response):
|
|
247
|
+
# Check if the callable accepts query_params
|
|
248
|
+
import inspect
|
|
249
|
+
sig = inspect.signature(route.response)
|
|
250
|
+
if 'query_params' in sig.parameters:
|
|
251
|
+
return route.response(path, method, headers, body, self, query_params)
|
|
233
252
|
return route.response(path, method, headers, body, self)
|
|
234
253
|
elif isinstance(route.response, str):
|
|
235
254
|
return {"status_code": route.status_code, "body": route.response}
|
|
236
255
|
else:
|
|
237
|
-
return {"status_code": route.status_code, "body": route.response}
|
|
256
|
+
return {"status_code": route.status_code, "body": route.response}
|
|
257
|
+
|