HTTP send

This commit is contained in:
현성필
2025-01-25 17:04:24 +09:00
parent 54c5633654
commit 0804087196
4 changed files with 222 additions and 24 deletions
@@ -15,7 +15,6 @@ const ServerManager = ({ onServerChange = () => {} }) => {
if (!initialized.current) {
initialized.current = true;
await loadServers();
console.log(servers);
}
};
@@ -10,7 +10,7 @@ import ServerManager from '@/components/ServerManager.jsx';
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@/components/ui/table.jsx';
import {useAPI} from '@/providers/APIProvider';
import MonacoEditor from '@/components/shared/MonacoEditor';
// import MonacoVariableEditor from './MonacoVariableEditor'; // Import the new component
import APIClient from '@/services/APIClient.js';
const HTTP_METHODS = [
'POST', 'GET', 'PUT', 'DELETE', 'CONNECT',
@@ -18,7 +18,9 @@ const HTTP_METHODS = [
];
export const HTTPClientView = ({model, index}) => {
const apiClient = new APIClient();
const {getServer} = useAPI();
const [isLoading, setIsLoading] = useState(false);
const [currentModel, setCurrentModel] = useState(model);
const [basePath, setBasePath] = useState('');
const [currentTab, setCurrentTab] = useState('body');
@@ -32,8 +34,8 @@ export const HTTPClientView = ({model, index}) => {
const [editorContents, setEditorContents] = useState({
requestBody: currentModel.requestBody || '',
responseBody: currentModel.responseBody || '',
console: '',
responseBody: '',
consoleLog: '',
preRequestScript: currentModel.preRequestScript || '',
postRequestScript: currentModel.postRequestScript || ''
});
@@ -101,6 +103,55 @@ export const HTTPClientView = ({model, index}) => {
</div>
);
const handleSendRequest = async () => {
if (isLoading) return;
setIsLoading(true);
setEditorContents(prev => ({...prev, consoleLog: ''}));
try {
const response = await apiClient.sendAPIRequest(
currentModel,
(log) => setEditorContents(prev => ({
...prev,
consoleLog: prev.consoleLog + log + '\n'
}))
);
const headerArray = Array.isArray(response.headers)
? response.headers
: Object.entries(response.headers).map(([key, value]) => ({
name: key,
value: value
}));
setResponseData({
headers: headerArray,
status: `Status: ${response.status}`,
time: `${response.time}ms`,
size: `Size: ${response.size}`
});
console.log(responseData);
setEditorContents(prev => ({
...prev,
responseBody: response.body
}));
console.log(editorContents.responseBody);
} catch (error) {
console.error(error);
setEditorContents(prev => ({
...prev,
consoleLog: prev.consoleLog + `Error: ${error.message}\n`
}));
} finally {
setIsLoading(false);
}
};
const renderResponseHeaders = () => (
<Table>
<TableHeader>
@@ -162,7 +213,12 @@ export const HTTPClientView = ({model, index}) => {
className="flex-1"
/>
<Button>Send</Button>
<Button
disabled={isLoading}
onClick={handleSendRequest}
>
{isLoading ? 'Sending...' : 'Send'}
</Button>
<Button variant="secondary">Save</Button>
</div>
@@ -185,18 +241,7 @@ export const HTTPClientView = ({model, index}) => {
<div className="flex flex-col h-full gap-2">
<Select
value={currentModel.contentType}
// onValueChange={(value) => setCurrentModel(prev => ({ ...prev, contentType: value }))}
onValueChange={(value) => {
// Update both content type and editor content
setCurrentModel(prev => ({
...prev,
contentType: value,
// Optionally format the content when switching to JSON
requestBody: value === 'json' && prev.requestBody ?
JSON.stringify(JSON.parse(prev.requestBody), null, 2) :
prev.requestBody
}));
}}
onValueChange={(value) => setCurrentModel(prev => ({ ...prev, contentType: value }))}
>
<SelectTrigger>
<SelectValue placeholder="Content Type"/>
@@ -207,7 +252,7 @@ export const HTTPClientView = ({model, index}) => {
<SelectItem value="plaintext">Text/Plain</SelectItem>
</SelectContent>
</Select>
<div className="h-full">
<div className="h-[calc(100%-40px)]">
<MonacoEditor
id={`request-body-${model.id}`}
value={currentModel.requestBody}
@@ -303,7 +348,7 @@ export const HTTPClientView = ({model, index}) => {
<TabsContent value="console" className="h-full m-0">
<MonacoEditor
id={`console-${model.id}`}
value={editorContents.console}
value={editorContents.consoleLog}
language="plaintext"
readOnly={true}
height="full"
@@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import { X } from "lucide-react";
import EditableInput from '@/components/shared/EditableInput.jsx';
const PropertyTable = ({
properties = [],
@@ -87,7 +88,7 @@ const PropertyTable = ({
/>
</TableCell>
<TableCell>
<Input
<EditableInput
value={property.key}
onChange={(e) =>
handlePropertyChange(index, 'key', e.target.value)
@@ -96,7 +97,7 @@ const PropertyTable = ({
/>
</TableCell>
<TableCell>
<Input
<EditableInput
value={property.value}
onChange={(e) =>
handlePropertyChange(index, 'value', e.target.value)
+156 -3
View File
@@ -18,16 +18,36 @@ class APIClient {
async sendAPIRequest(model, onLog = () => {}) {
try {
onLog(`Request: ${model.method} ${model.path}`);
let variables = {};
const processedModel = await this.executePreRequestScript(model, variables, onLog);
const processedRequest = this.replacePlaceholdersWithVariables(processedModel, variables);
onLog(`Request: ${processedRequest.method} ${processedRequest.path}`);
const response = await this.makeRequest(model);
const formattedResponse = this.formatResponse(response);
return formattedResponse;
onLog(formattedResponse);
const finalResponse = await this.executePostRequestScript(
model.postRequestScript,
variables,
processedRequest,
formattedResponse,
onLog
);
return finalResponse;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request was cancelled');
}
throw new Error(`API Error: ${error.message}`);
const errorMessage = error.message || 'Unknown error';
onLog(`Error: ${errorMessage}`);
throw new Error(`API Error: ${errorMessage}`);
} finally {
this.abortController = null;
}
@@ -66,6 +86,139 @@ class APIClient {
getClientUrl() {
return this.contextPath + this.CLIENT_URL;
}
replacePlaceholdersWithVariables(original, variables) {
const request = JSON.parse(JSON.stringify(original));
const mergedVariables = { ...this.globals, ...variables };
Object.entries(mergedVariables).forEach(([key, value]) => {
const placeholder = new RegExp(`{{${key}}}`, 'g');
request.path = request.path?.replace(placeholder, value);
request.requestBody = request.requestBody?.replace(placeholder, value);
if (request.headers) {
const headersStr = JSON.stringify(request.headers).replace(placeholder, value);
request.headers = JSON.parse(headersStr);
}
if (request.queryParams) {
const paramsStr = JSON.stringify(request.queryParams).replace(placeholder, value);
request.queryParams = JSON.parse(paramsStr);
}
});
return request;
}
async executePreRequestScript(model, variables, onLog) {
return new Promise((resolve, reject) => {
// Create sandbox iframe if not exists
let sandbox = document.getElementById('preRequest');
if (!sandbox) {
sandbox = document.createElement('iframe');
sandbox.id = 'preRequest';
sandbox.style.display = 'none';
document.body.appendChild(sandbox);
}
// Setup communication channel
window.preRequestComplete = (updatedModel, error) => {
if (error) {
reject(error);
} else {
resolve(updatedModel);
}
};
// Setup context
window.temp_variable = variables;
window.currentRequest = model;
window.consoleOutput = onLog;
// Create sandbox content
const sandboxContent = `
<script>
var oldLog = console.log;
console.log = function(message) {
if (window.parent.consoleOutput) {
window.parent.consoleOutput(message);
}
oldLog.apply(console, arguments);
};
window.globals = window.parent.globals;
window.variables = window.parent.temp_variable;
window.request = window.parent.currentRequest;
try {
${model.preRequestScript || ''}
window.parent.preRequestComplete(window.request, null);
} catch (error) {
window.parent.preRequestComplete(null, error);
}
</script>
`;
sandbox.srcdoc = sandboxContent;
});
}
async executePostRequestScript(script, variables, request, response, onLog) {
return new Promise((resolve, reject) => {
// Create sandbox iframe if not exists
let sandbox = document.getElementById('postRequest');
if (!sandbox) {
sandbox = document.createElement('iframe');
sandbox.id = 'postRequest';
sandbox.style.display = 'none';
document.body.appendChild(sandbox);
}
// Setup communication channel
window.postRequestComplete = (updatedResponse, error) => {
if (error) {
reject(error);
} else {
resolve(updatedResponse);
}
};
// Setup context
window.request = request;
window.response = response;
window.temp_variable = variables;
window.consoleOutput = onLog;
// Create sandbox content
const sandboxContent = `
<script>
var oldLog = console.log;
console.log = function(message) {
if (window.parent.consoleOutput) {
window.parent.consoleOutput(message);
}
oldLog.apply(console, arguments);
};
window.globals = window.parent.globals;
window.variables = window.parent.temp_variable;
window.request = window.parent.request;
window.response = window.parent.response;
try {
${script || ''}
window.parent.postRequestComplete(window.response, null);
} catch (error) {
window.parent.postRequestComplete(window.response, error.toString());
}
</script>
`;
sandbox.srcdoc = sandboxContent;
});
}
}
export default APIClient;