Skip to content

Commit d8067f0

Browse files
Add form-urlencoded content-type support for gateway testing (#1378)
* add form-urlencoded support for OAuth2 gateway testing Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * fix flake8 Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * fix formatting Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * chore: apply formatting fixes from pre-commit hooks Apply black and trailing-whitespace fixes after rebase. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Shoumi <shoumimukherjee@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
1 parent 209d31d commit d8067f0

File tree

5 files changed

+79
-4
lines changed

5 files changed

+79
-4
lines changed

mcpgateway/admin.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8292,8 +8292,27 @@ async def admin_test_gateway(request: GatewayTestRequest, team_id: Optional[str]
82928292
else:
82938293
headers: dict = decode_auth(gateway.auth_value if gateway else None)
82948294

8295+
# Prepare request based on content type
8296+
content_type = getattr(request, "content_type", "application/json")
8297+
request_kwargs = {"method": request.method.upper(), "url": full_url, "headers": headers}
8298+
8299+
if request.body is not None:
8300+
if content_type == "application/x-www-form-urlencoded":
8301+
# Set proper content type header and use data parameter for form encoding
8302+
headers["Content-Type"] = "application/x-www-form-urlencoded"
8303+
if isinstance(request.body, str):
8304+
# Body is already form-encoded
8305+
request_kwargs["data"] = request.body
8306+
else:
8307+
# Body is a dict, convert to form data
8308+
request_kwargs["data"] = request.body
8309+
else:
8310+
# Default to JSON
8311+
headers["Content-Type"] = "application/json"
8312+
request_kwargs["json"] = request.body
8313+
82958314
async with ResilientHttpClient(client_args={"timeout": settings.federation_timeout, "verify": not settings.skip_ssl_verify}) as client:
8296-
response: httpx.Response = await client.request(method=request.method.upper(), url=full_url, headers=headers, json=request.body)
8315+
response: httpx.Response = await client.request(**request_kwargs)
82978316
latency_ms = int((time.monotonic() - start_time) * 1000)
82988317
try:
82998318
response_body: Union[Dict[str, Any], str] = response.json()

mcpgateway/schemas.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3756,14 +3756,15 @@ def populate_associated_ids(cls, values):
37563756
class GatewayTestRequest(BaseModelWithConfigDict):
37573757
"""Schema for testing gateway connectivity.
37583758
3759-
Includes the HTTP method, base URL, path, optional headers, and body.
3759+
Includes the HTTP method, base URL, path, optional headers, body, and content type.
37603760
"""
37613761

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

37683769

37693770
class GatewayTestResponse(BaseModelWithConfigDict):

mcpgateway/static/admin.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8060,6 +8060,7 @@ async function handleGatewayTestSubmit(e) {
80608060
const baseUrl = formData.get("url");
80618061
const method = formData.get("method");
80628062
const path = formData.get("path");
8063+
const contentType = formData.get("content_type") || "application/json";
80638064

80648065
// Validate URL
80658066
const urlValidation = validateUrl(baseUrl);
@@ -8099,12 +8100,28 @@ async function handleGatewayTestSubmit(e) {
80998100
throw new Error(bodyValidation.error);
81008101
}
81018102

8103+
// Process body based on content type
8104+
let processedBody = bodyValidation.value;
8105+
if (
8106+
contentType === "application/x-www-form-urlencoded" &&
8107+
bodyValidation.value &&
8108+
typeof bodyValidation.value === "object"
8109+
) {
8110+
// Convert JSON object to URL-encoded string
8111+
const params = new URLSearchParams();
8112+
Object.entries(bodyValidation.value).forEach(([key, value]) => {
8113+
params.append(key, String(value));
8114+
});
8115+
processedBody = params.toString();
8116+
}
8117+
81028118
const payload = {
81038119
base_url: urlValidation.value,
81048120
method,
81058121
path,
81068122
headers: headersValidation.value,
8107-
body: bodyValidation.value,
8123+
body: processedBody,
8124+
content_type: contentType,
81088125
};
81098126

81108127
// Make the request with timeout
@@ -17556,3 +17573,21 @@ style.textContent = `
1755617573
}
1755717574
`;
1755817575
document.head.appendChild(style);
17576+
17577+
// Function to update body label based on content type selection
17578+
function updateBodyLabel() {
17579+
const bodyLabel = document.getElementById("gateway-test-body-label");
17580+
const contentType = document.getElementById(
17581+
"gateway-test-content-type",
17582+
)?.value;
17583+
17584+
if (bodyLabel) {
17585+
bodyLabel.innerHTML =
17586+
contentType === "application/x-www-form-urlencoded"
17587+
? 'Body (JSON)<br><small class="text-gray-500">Auto-converts to form data</small>'
17588+
: "Body (JSON)";
17589+
}
17590+
}
17591+
17592+
// Make it available globally for HTML onclick handlers
17593+
window.updateBodyLabel = updateBodyLabel;

mcpgateway/templates/admin.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7781,6 +7781,7 @@ <h2 class="text-xl font-bold mb-4 text-gray-800 dark:text-gray-100">
77817781
<form
77827782
id="gateway-test-form"
77837783
class="space-y-4 text-gray-800 dark:text-gray-100"
7784+
novalidate
77847785
>
77857786
<div>
77867787
<label
@@ -7842,14 +7843,33 @@ <h2 class="text-xl font-bold mb-4 text-gray-800 dark:text-gray-100">
78427843
></textarea>
78437844
</div>
78447845

7846+
<div>
7847+
<label
7848+
for="gateway-test-content-type"
7849+
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
7850+
>Content-Type</label
7851+
>
7852+
<select
7853+
id="gateway-test-content-type"
7854+
name="content_type"
7855+
class="mt-1 block w-full rounded-md shadow-sm p-1 bg-gray-200 dark:bg-gray-800"
7856+
onchange="updateBodyLabel()"
7857+
>
7858+
<option value="application/json">application/json</option>
7859+
<option value="application/x-www-form-urlencoded">application/x-www-form-urlencoded</option>
7860+
</select>
7861+
</div>
7862+
78457863
<div>
78467864
<label
78477865
for="gateway-test-body"
78487866
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
7867+
id="gateway-test-body-label"
78497868
>Body (JSON)</label
78507869
>
78517870
<textarea
78527871
id="gateway-test-body"
7872+
name="body"
78537873
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"
78547874
placeholder="Enter request body as JSON"
78557875
></textarea>

tests/e2e/test_main_apis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,7 @@ async def test_create_json_resource(self, client: AsyncClient, mock_auth):
970970
assert result["mime_type"] == "application/json"
971971
elif "mimeType" in result:
972972
assert result["mimeType"] == "application/json"
973-
973+
974974

975975
async def test_create_resource_form_urlencoded(self, client: AsyncClient, mock_auth):
976976
"""

0 commit comments

Comments
 (0)