HTTP send
This commit is contained in:
@@ -15,7 +15,6 @@ const ServerManager = ({ onServerChange = () => {} }) => {
|
|||||||
if (!initialized.current) {
|
if (!initialized.current) {
|
||||||
initialized.current = true;
|
initialized.current = true;
|
||||||
await loadServers();
|
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 {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@/components/ui/table.jsx';
|
||||||
import {useAPI} from '@/providers/APIProvider';
|
import {useAPI} from '@/providers/APIProvider';
|
||||||
import MonacoEditor from '@/components/shared/MonacoEditor';
|
import MonacoEditor from '@/components/shared/MonacoEditor';
|
||||||
// import MonacoVariableEditor from './MonacoVariableEditor'; // Import the new component
|
import APIClient from '@/services/APIClient.js';
|
||||||
|
|
||||||
const HTTP_METHODS = [
|
const HTTP_METHODS = [
|
||||||
'POST', 'GET', 'PUT', 'DELETE', 'CONNECT',
|
'POST', 'GET', 'PUT', 'DELETE', 'CONNECT',
|
||||||
@@ -18,7 +18,9 @@ const HTTP_METHODS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const HTTPClientView = ({model, index}) => {
|
export const HTTPClientView = ({model, index}) => {
|
||||||
|
const apiClient = new APIClient();
|
||||||
const {getServer} = useAPI();
|
const {getServer} = useAPI();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [currentModel, setCurrentModel] = useState(model);
|
const [currentModel, setCurrentModel] = useState(model);
|
||||||
const [basePath, setBasePath] = useState('');
|
const [basePath, setBasePath] = useState('');
|
||||||
const [currentTab, setCurrentTab] = useState('body');
|
const [currentTab, setCurrentTab] = useState('body');
|
||||||
@@ -32,8 +34,8 @@ export const HTTPClientView = ({model, index}) => {
|
|||||||
|
|
||||||
const [editorContents, setEditorContents] = useState({
|
const [editorContents, setEditorContents] = useState({
|
||||||
requestBody: currentModel.requestBody || '',
|
requestBody: currentModel.requestBody || '',
|
||||||
responseBody: currentModel.responseBody || '',
|
responseBody: '',
|
||||||
console: '',
|
consoleLog: '',
|
||||||
preRequestScript: currentModel.preRequestScript || '',
|
preRequestScript: currentModel.preRequestScript || '',
|
||||||
postRequestScript: currentModel.postRequestScript || ''
|
postRequestScript: currentModel.postRequestScript || ''
|
||||||
});
|
});
|
||||||
@@ -101,6 +103,55 @@ export const HTTPClientView = ({model, index}) => {
|
|||||||
</div>
|
</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 = () => (
|
const renderResponseHeaders = () => (
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
@@ -162,7 +213,12 @@ export const HTTPClientView = ({model, index}) => {
|
|||||||
className="flex-1"
|
className="flex-1"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button>Send</Button>
|
<Button
|
||||||
|
disabled={isLoading}
|
||||||
|
onClick={handleSendRequest}
|
||||||
|
>
|
||||||
|
{isLoading ? 'Sending...' : 'Send'}
|
||||||
|
</Button>
|
||||||
<Button variant="secondary">Save</Button>
|
<Button variant="secondary">Save</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -185,18 +241,7 @@ export const HTTPClientView = ({model, index}) => {
|
|||||||
<div className="flex flex-col h-full gap-2">
|
<div className="flex flex-col h-full gap-2">
|
||||||
<Select
|
<Select
|
||||||
value={currentModel.contentType}
|
value={currentModel.contentType}
|
||||||
// onValueChange={(value) => setCurrentModel(prev => ({ ...prev, contentType: value }))}
|
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
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Content Type"/>
|
<SelectValue placeholder="Content Type"/>
|
||||||
@@ -207,7 +252,7 @@ export const HTTPClientView = ({model, index}) => {
|
|||||||
<SelectItem value="plaintext">Text/Plain</SelectItem>
|
<SelectItem value="plaintext">Text/Plain</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<div className="h-full">
|
<div className="h-[calc(100%-40px)]">
|
||||||
<MonacoEditor
|
<MonacoEditor
|
||||||
id={`request-body-${model.id}`}
|
id={`request-body-${model.id}`}
|
||||||
value={currentModel.requestBody}
|
value={currentModel.requestBody}
|
||||||
@@ -303,7 +348,7 @@ export const HTTPClientView = ({model, index}) => {
|
|||||||
<TabsContent value="console" className="h-full m-0">
|
<TabsContent value="console" className="h-full m-0">
|
||||||
<MonacoEditor
|
<MonacoEditor
|
||||||
id={`console-${model.id}`}
|
id={`console-${model.id}`}
|
||||||
value={editorContents.console}
|
value={editorContents.consoleLog}
|
||||||
language="plaintext"
|
language="plaintext"
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
height="full"
|
height="full"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
|
import EditableInput from '@/components/shared/EditableInput.jsx';
|
||||||
|
|
||||||
const PropertyTable = ({
|
const PropertyTable = ({
|
||||||
properties = [],
|
properties = [],
|
||||||
@@ -87,7 +88,7 @@ const PropertyTable = ({
|
|||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Input
|
<EditableInput
|
||||||
value={property.key}
|
value={property.key}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handlePropertyChange(index, 'key', e.target.value)
|
handlePropertyChange(index, 'key', e.target.value)
|
||||||
@@ -96,7 +97,7 @@ const PropertyTable = ({
|
|||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Input
|
<EditableInput
|
||||||
value={property.value}
|
value={property.value}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handlePropertyChange(index, 'value', e.target.value)
|
handlePropertyChange(index, 'value', e.target.value)
|
||||||
|
|||||||
@@ -18,16 +18,36 @@ class APIClient {
|
|||||||
|
|
||||||
async sendAPIRequest(model, onLog = () => {}) {
|
async sendAPIRequest(model, onLog = () => {}) {
|
||||||
try {
|
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 response = await this.makeRequest(model);
|
||||||
const formattedResponse = this.formatResponse(response);
|
const formattedResponse = this.formatResponse(response);
|
||||||
return formattedResponse;
|
onLog(formattedResponse);
|
||||||
|
|
||||||
|
const finalResponse = await this.executePostRequestScript(
|
||||||
|
model.postRequestScript,
|
||||||
|
variables,
|
||||||
|
processedRequest,
|
||||||
|
formattedResponse,
|
||||||
|
onLog
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return finalResponse;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name === 'AbortError') {
|
if (error.name === 'AbortError') {
|
||||||
throw new Error('Request was cancelled');
|
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 {
|
} finally {
|
||||||
this.abortController = null;
|
this.abortController = null;
|
||||||
}
|
}
|
||||||
@@ -66,6 +86,139 @@ class APIClient {
|
|||||||
getClientUrl() {
|
getClientUrl() {
|
||||||
return this.contextPath + this.CLIENT_URL;
|
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;
|
export default APIClient;
|
||||||
|
|||||||
Reference in New Issue
Block a user