feat: render all fields in the forms
This commit is contained in:
162
public/app.js
162
public/app.js
@@ -196,20 +196,44 @@ paths:
|
||||
form.className = 'op-form';
|
||||
|
||||
const pathParams = buildPathParams(op.path);
|
||||
const allParams = (op.parameters || []).filter(p => p.in === 'path' || p.in === 'query');
|
||||
const pathParamsWithSchema = pathParams.map(name => {
|
||||
const param = allParams.find(p => p.in === 'path' && p.name === name);
|
||||
return { name, schema: param ? (param.schema || null) : null };
|
||||
});
|
||||
const queryParams = (op.parameters || []).filter(p => p.in === 'query');
|
||||
|
||||
const row1 = document.createElement('div');
|
||||
row1.className = 'row';
|
||||
for (const name of pathParams) {
|
||||
for (const paramInfo of pathParamsWithSchema) {
|
||||
const name = paramInfo.name;
|
||||
const schema = paramInfo.schema || {};
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'inline';
|
||||
const label = document.createElement('label');
|
||||
label.textContent = `path: ${name}`;
|
||||
const input = document.createElement('input');
|
||||
input.name = `path:${name}`;
|
||||
input.placeholder = name;
|
||||
wrap.appendChild(label);
|
||||
wrap.appendChild(input);
|
||||
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
||||
const select = document.createElement('select');
|
||||
select.name = `path:${name}`;
|
||||
const emptyOpt = document.createElement('option');
|
||||
emptyOpt.value = '';
|
||||
emptyOpt.textContent = '-- select --';
|
||||
select.appendChild(emptyOpt);
|
||||
for (const val of schema.enum) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(val);
|
||||
opt.textContent = String(val);
|
||||
select.appendChild(opt);
|
||||
}
|
||||
wrap.appendChild(label);
|
||||
wrap.appendChild(select);
|
||||
} else {
|
||||
const input = document.createElement('input');
|
||||
input.name = `path:${name}`;
|
||||
input.placeholder = name;
|
||||
wrap.appendChild(label);
|
||||
wrap.appendChild(input);
|
||||
}
|
||||
row1.appendChild(wrap);
|
||||
}
|
||||
for (const p of queryParams) {
|
||||
@@ -217,24 +241,79 @@ paths:
|
||||
wrap.className = 'inline';
|
||||
const label = document.createElement('label');
|
||||
label.textContent = `query: ${p.name}`;
|
||||
const input = document.createElement('input');
|
||||
input.name = `query:${p.name}`;
|
||||
input.placeholder = p.name;
|
||||
wrap.appendChild(label);
|
||||
wrap.appendChild(input);
|
||||
const schema = p.schema || {};
|
||||
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
||||
const select = document.createElement('select');
|
||||
select.name = `query:${p.name}`;
|
||||
const emptyOpt = document.createElement('option');
|
||||
emptyOpt.value = '';
|
||||
emptyOpt.textContent = '-- select --';
|
||||
select.appendChild(emptyOpt);
|
||||
for (const val of schema.enum) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(val);
|
||||
opt.textContent = String(val);
|
||||
select.appendChild(opt);
|
||||
}
|
||||
wrap.appendChild(label);
|
||||
wrap.appendChild(select);
|
||||
} else {
|
||||
const input = document.createElement('input');
|
||||
input.name = `query:${p.name}`;
|
||||
input.placeholder = p.name;
|
||||
wrap.appendChild(label);
|
||||
wrap.appendChild(input);
|
||||
}
|
||||
row1.appendChild(wrap);
|
||||
}
|
||||
form.appendChild(row1);
|
||||
|
||||
if (op.hasJsonBody) {
|
||||
const label = document.createElement('label');
|
||||
label.textContent = 'JSON body';
|
||||
const ta = document.createElement('textarea');
|
||||
ta.name = 'body';
|
||||
ta.rows = 5;
|
||||
ta.placeholder = '{\n \"example\": true\n}';
|
||||
form.appendChild(label);
|
||||
form.appendChild(ta);
|
||||
if (Array.isArray(op.bodyFields) && op.bodyFields.length > 0) {
|
||||
const label = document.createElement('label');
|
||||
label.textContent = 'Body fields';
|
||||
form.appendChild(label);
|
||||
for (const f of op.bodyFields) {
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'inline';
|
||||
const l = document.createElement('label');
|
||||
l.textContent = `${f.required ? '*' : ''}${f.name}`;
|
||||
if (Array.isArray(f.enum) && f.enum.length > 0) {
|
||||
const select = document.createElement('select');
|
||||
select.name = `body:${f.name}`;
|
||||
if (!f.required) {
|
||||
const emptyOpt = document.createElement('option');
|
||||
emptyOpt.value = '';
|
||||
emptyOpt.textContent = '-- select --';
|
||||
select.appendChild(emptyOpt);
|
||||
}
|
||||
for (const val of f.enum) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(val);
|
||||
opt.textContent = String(val);
|
||||
select.appendChild(opt);
|
||||
}
|
||||
wrap.appendChild(l);
|
||||
wrap.appendChild(select);
|
||||
} else {
|
||||
const input = document.createElement('input');
|
||||
input.name = `body:${f.name}`;
|
||||
input.placeholder = f.type || 'string';
|
||||
wrap.appendChild(l);
|
||||
wrap.appendChild(input);
|
||||
}
|
||||
form.appendChild(wrap);
|
||||
}
|
||||
} else {
|
||||
const label = document.createElement('label');
|
||||
label.textContent = 'JSON body';
|
||||
const ta = document.createElement('textarea');
|
||||
ta.name = 'body';
|
||||
ta.rows = 5;
|
||||
ta.placeholder = '{\n "example": true\n}';
|
||||
form.appendChild(label);
|
||||
form.appendChild(ta);
|
||||
}
|
||||
}
|
||||
|
||||
const submit = document.createElement('button');
|
||||
@@ -272,15 +351,54 @@ paths:
|
||||
}
|
||||
let body = undefined;
|
||||
if (op.hasJsonBody) {
|
||||
const raw = formData.get('body');
|
||||
body = raw ? JSON.parse(raw) : undefined;
|
||||
if (Array.isArray(op.bodyFields) && op.bodyFields.length > 0) {
|
||||
const obj = {};
|
||||
const isFormUrlEncoded = op.bodyContentType === 'application/x-www-form-urlencoded';
|
||||
if (isFormUrlEncoded) {
|
||||
// For form-urlencoded, keep it flat (no nesting)
|
||||
for (const [k, v] of formData.entries()) {
|
||||
if (k.startsWith('body:')) {
|
||||
const name = k.slice(5);
|
||||
// For form-urlencoded, only use the top-level field name (ignore dots)
|
||||
const topLevelName = name.split('.')[0];
|
||||
if (v !== '') obj[topLevelName] = v;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For JSON, allow nesting via dot notation
|
||||
const setDeep = (o, path, value) => {
|
||||
const parts = path.split('.');
|
||||
let cur = o;
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const key = parts[i];
|
||||
const isLast = i === parts.length - 1;
|
||||
if (isLast) {
|
||||
cur[key] = value;
|
||||
} else {
|
||||
if (typeof cur[key] !== 'object' || cur[key] === null) cur[key] = {};
|
||||
cur = cur[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const [k, v] of formData.entries()) {
|
||||
if (k.startsWith('body:')) {
|
||||
const name = k.slice(5);
|
||||
if (v !== '') setDeep(obj, name, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
body = Object.keys(obj).length ? obj : undefined;
|
||||
} else {
|
||||
const raw = formData.get('body');
|
||||
body = raw ? JSON.parse(raw) : undefined;
|
||||
}
|
||||
}
|
||||
const headers = collectCustomHeaders();
|
||||
result.textContent = 'Loading...';
|
||||
const resp = await fetch('/api/proxy', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ baseUrl, method: op.method, path: finalPath, query, body, headers })
|
||||
body: JSON.stringify({ baseUrl, method: op.method, path: finalPath, query, body, headers, contentType: op.bodyContentType || null })
|
||||
});
|
||||
const data = await resp.json();
|
||||
result.textContent = JSON.stringify(data, null, 2);
|
||||
|
||||
Reference in New Issue
Block a user