Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion mcpgateway/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8292,8 +8292,27 @@ async def admin_test_gateway(request: GatewayTestRequest, team_id: Optional[str]
else:
headers: dict = decode_auth(gateway.auth_value if gateway else None)

# Prepare request based on content type
content_type = getattr(request, "content_type", "application/json")
request_kwargs = {"method": request.method.upper(), "url": full_url, "headers": headers}

if request.body is not None:
if content_type == "application/x-www-form-urlencoded":
# Set proper content type header and use data parameter for form encoding
headers["Content-Type"] = "application/x-www-form-urlencoded"
if isinstance(request.body, str):
# Body is already form-encoded
request_kwargs["data"] = request.body
else:
# Body is a dict, convert to form data
request_kwargs["data"] = request.body
else:
# Default to JSON
headers["Content-Type"] = "application/json"
request_kwargs["json"] = request.body

async with ResilientHttpClient(client_args={"timeout": settings.federation_timeout, "verify": not settings.skip_ssl_verify}) as client:
response: httpx.Response = await client.request(method=request.method.upper(), url=full_url, headers=headers, json=request.body)
response: httpx.Response = await client.request(**request_kwargs)
latency_ms = int((time.monotonic() - start_time) * 1000)
try:
response_body: Union[Dict[str, Any], str] = response.json()
Expand Down
3 changes: 2 additions & 1 deletion mcpgateway/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -3756,14 +3756,15 @@ def populate_associated_ids(cls, values):
class GatewayTestRequest(BaseModelWithConfigDict):
"""Schema for testing gateway connectivity.

Includes the HTTP method, base URL, path, optional headers, and body.
Includes the HTTP method, base URL, path, optional headers, body, and content type.
"""

method: str = Field(..., description="HTTP method to test (GET, POST, etc.)")
base_url: AnyHttpUrl = Field(..., description="Base URL of the gateway to test")
path: str = Field(..., description="Path to append to the base URL")
headers: Optional[Dict[str, str]] = Field(None, description="Optional headers for the request")
body: Optional[Union[str, Dict[str, Any]]] = Field(None, description="Optional body for the request, can be a string or JSON object")
content_type: Optional[str] = Field("application/json", description="Content type for the request body")


class GatewayTestResponse(BaseModelWithConfigDict):
Expand Down
37 changes: 36 additions & 1 deletion mcpgateway/static/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -8060,6 +8060,7 @@ async function handleGatewayTestSubmit(e) {
const baseUrl = formData.get("url");
const method = formData.get("method");
const path = formData.get("path");
const contentType = formData.get("content_type") || "application/json";

// Validate URL
const urlValidation = validateUrl(baseUrl);
Expand Down Expand Up @@ -8099,12 +8100,28 @@ async function handleGatewayTestSubmit(e) {
throw new Error(bodyValidation.error);
}

// Process body based on content type
let processedBody = bodyValidation.value;
if (
contentType === "application/x-www-form-urlencoded" &&
bodyValidation.value &&
typeof bodyValidation.value === "object"
) {
// Convert JSON object to URL-encoded string
const params = new URLSearchParams();
Object.entries(bodyValidation.value).forEach(([key, value]) => {
params.append(key, String(value));
});
processedBody = params.toString();
}

const payload = {
base_url: urlValidation.value,
method,
path,
headers: headersValidation.value,
body: bodyValidation.value,
body: processedBody,
content_type: contentType,
};

// Make the request with timeout
Expand Down Expand Up @@ -17556,3 +17573,21 @@ style.textContent = `
}
`;
document.head.appendChild(style);

// Function to update body label based on content type selection
function updateBodyLabel() {
const bodyLabel = document.getElementById("gateway-test-body-label");
const contentType = document.getElementById(
"gateway-test-content-type",
)?.value;

if (bodyLabel) {
bodyLabel.innerHTML =
contentType === "application/x-www-form-urlencoded"
? 'Body (JSON)<br><small class="text-gray-500">Auto-converts to form data</small>'
: "Body (JSON)";
}
}

// Make it available globally for HTML onclick handlers
window.updateBodyLabel = updateBodyLabel;
20 changes: 20 additions & 0 deletions mcpgateway/templates/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -7781,6 +7781,7 @@ <h2 class="text-xl font-bold mb-4 text-gray-800 dark:text-gray-100">
<form
id="gateway-test-form"
class="space-y-4 text-gray-800 dark:text-gray-100"
novalidate
>
<div>
<label
Expand Down Expand Up @@ -7842,14 +7843,33 @@ <h2 class="text-xl font-bold mb-4 text-gray-800 dark:text-gray-100">
></textarea>
</div>

<div>
<label
for="gateway-test-content-type"
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
>Content-Type</label
>
<select
id="gateway-test-content-type"
name="content_type"
class="mt-1 block w-full rounded-md shadow-sm p-1 bg-gray-200 dark:bg-gray-800"
onchange="updateBodyLabel()"
>
<option value="application/json">application/json</option>
<option value="application/x-www-form-urlencoded">application/x-www-form-urlencoded</option>
</select>
</div>

<div>
<label
for="gateway-test-body"
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
id="gateway-test-body-label"
>Body (JSON)</label
>
<textarea
id="gateway-test-body"
name="body"
class="mt-1 block w-full rounded-md shadow-sm p-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-300 placeholder-gray-600 dark:placeholder-gray-400"
placeholder="Enter request body as JSON"
></textarea>
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/test_main_apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ async def test_create_json_resource(self, client: AsyncClient, mock_auth):
assert result["mime_type"] == "application/json"
elif "mimeType" in result:
assert result["mimeType"] == "application/json"


async def test_create_resource_form_urlencoded(self, client: AsyncClient, mock_auth):
"""
Expand Down
Loading