Update Ui for adding method to passthrough endpoints

This commit is contained in:
Sameer Kankute
2026-02-19 11:59:36 +05:30
parent 5bd7bf1b3e
commit 26c3d7debc
4 changed files with 147 additions and 7 deletions
@@ -30,6 +30,8 @@ import PassThroughSecuritySection from "./common_components/PassThroughSecurityS
import PassThroughGuardrailsSection from "./common_components/PassThroughGuardrailsSection";
const { Option } = Select2;
const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
interface AddFallbacksProps {
// models: string[] | undefined;
accessToken: string;
@@ -52,12 +54,14 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
const [targetValue, setTargetValue] = useState("");
const [includeSubpath, setIncludeSubpath] = useState(true);
const [authEnabled, setAuthEnabled] = useState(false);
const [selectedMethods, setSelectedMethods] = useState<string[]>([]);
const [guardrails, setGuardrails] = useState<Record<string, { request_fields?: string[]; response_fields?: string[] } | null>>({});
const handleCancel = () => {
form.resetFields();
setPathValue("");
setTargetValue("");
setIncludeSubpath(true);
setSelectedMethods([]);
setGuardrails({});
setIsModalVisible(false);
};
@@ -86,6 +90,11 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
formValues.guardrails = guardrails;
}
// Add methods to formValues (only if specific methods are selected)
if (selectedMethods && selectedMethods.length > 0) {
formValues.methods = selectedMethods;
}
console.log(`formValues: ${JSON.stringify(formValues)}`);
const response = await createPassThroughEndpoint(accessToken, formValues);
@@ -101,6 +110,7 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
setPathValue("");
setTargetValue("");
setIncludeSubpath(true);
setSelectedMethods([]);
setGuardrails({});
setIsModalVisible(false);
} catch (error) {
@@ -204,6 +214,41 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
/>
</Form.Item>
<Form.Item
label={
<span className="text-sm font-medium text-gray-700 flex items-center">
HTTP Methods (Optional)
<Tooltip title="Select specific HTTP methods. Leave empty to support all methods (GET, POST, PUT, DELETE, PATCH). Useful when the same path needs different targets for different methods.">
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
</Tooltip>
</span>
}
name="methods"
extra={
<div className="text-xs text-gray-500 mt-1">
{selectedMethods.length === 0
? "All HTTP methods supported (default)"
: `Only ${selectedMethods.join(", ")} requests will be routed to this endpoint`}
</div>
}
className="mb-4"
>
<Select2
mode="multiple"
placeholder="Select methods (leave empty for all)"
value={selectedMethods}
onChange={setSelectedMethods}
allowClear
style={{ width: "100%" }}
>
{HTTP_METHODS.map((method) => (
<Option key={method} value={method}>
{method}
</Option>
))}
</Select2>
</Form.Item>
<div className="flex items-center justify-between py-3">
<div>
<div className="text-sm font-medium text-gray-700">Include Subpaths</div>
@@ -12,6 +12,11 @@ interface PassThroughRoutesSelectorProps {
teamId?: string | null;
}
interface PassThroughEndpoint {
path: string;
methods?: string[];
}
const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
onChange,
value,
@@ -21,7 +26,7 @@ const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
disabled = false,
teamId,
}) => {
const [passThroughRoutes, setPassThroughRoutes] = useState<string[]>([]);
const [passThroughRoutes, setPassThroughRoutes] = useState<Array<{ label: string; value: string }>>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
@@ -32,7 +37,24 @@ const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
try {
const response = await getPassThroughEndpointsCall(accessToken, teamId);
if (response.endpoints) {
const routes = response.endpoints.map((route: { path: string }) => route.path);
const routes = response.endpoints.flatMap((endpoint: PassThroughEndpoint) => {
const path = endpoint.path;
const methods = endpoint.methods;
// If methods are specified, create one entry per method
if (methods && methods.length > 0) {
return methods.map((method) => ({
label: `${method} ${path}`,
value: path, // Keep value as path for backward compatibility
}));
}
// If no methods specified, show just the path (all methods supported)
return [{
label: path,
value: path,
}];
});
setPassThroughRoutes(routes);
}
} catch (error) {
@@ -54,10 +76,7 @@ const PassThroughRoutesSelector: React.FC<PassThroughRoutesSelectorProps> = ({
loading={loading}
className={className}
allowClear
options={passThroughRoutes.map((route) => ({
label: route,
value: route,
}))}
options={passThroughRoutes}
optionFilterProp="label"
showSearch
style={{ width: "100%" }}
@@ -13,7 +13,7 @@ import {
TabPanels,
TextInput,
} from "@tremor/react";
import { Button, Form, Input, Switch, InputNumber } from "antd";
import { Button, Form, Input, Switch, InputNumber, Select } from "antd";
import { updatePassThroughEndpoint, deletePassThroughEndpointsCall } from "./networking";
import { Eye, EyeOff } from "lucide-react";
import RoutePreview from "./route_preview";
@@ -21,6 +21,9 @@ import NotificationsManager from "./molecules/notifications_manager";
import PassThroughSecuritySection from "./common_components/PassThroughSecuritySection";
import PassThroughGuardrailsSection from "./common_components/PassThroughGuardrailsSection";
const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
const { Option } = Select;
export interface PassThroughInfoProps {
endpointData: PassThroughEndpoint;
onClose: () => void;
@@ -38,6 +41,7 @@ interface PassThroughEndpoint {
include_subpath?: boolean;
cost_per_request?: number;
auth?: boolean;
methods?: string[];
guardrails?: Record<string, { request_fields?: string[]; response_fields?: string[] } | null>;
}
@@ -70,6 +74,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
const [loading, setLoading] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [authEnabled, setAuthEnabled] = useState(initialEndpointData?.auth || false);
const [selectedMethods, setSelectedMethods] = useState<string[]>(initialEndpointData?.methods || []);
const [guardrails, setGuardrails] = useState<Record<string, { request_fields?: string[]; response_fields?: string[] } | null>>(
initialEndpointData?.guardrails || {}
);
@@ -97,6 +102,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
include_subpath: values.include_subpath,
cost_per_request: values.cost_per_request,
auth: premiumUser ? values.auth : undefined,
methods: selectedMethods && selectedMethods.length > 0 ? selectedMethods : undefined,
guardrails: guardrails && Object.keys(guardrails).length > 0 ? guardrails : undefined,
};
@@ -191,6 +197,23 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
{endpointData.auth ? "Auth Required" : "No Auth"}
</Badge>
</div>
{endpointData.methods && endpointData.methods.length > 0 && (
<div>
<Text className="text-xs text-gray-500">HTTP Methods:</Text>
<div className="flex flex-wrap gap-1 mt-1">
{endpointData.methods.map((method) => (
<Badge key={method} color="indigo" size="sm">
{method}
</Badge>
))}
</div>
</div>
)}
{(!endpointData.methods || endpointData.methods.length === 0) && (
<div>
<Text className="text-xs text-gray-500">All HTTP methods supported</Text>
</div>
)}
{endpointData.cost_per_request !== undefined && (
<div>
<Text>Cost per request: ${endpointData.cost_per_request}</Text>
@@ -277,6 +300,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
include_subpath: endpointData.include_subpath || false,
cost_per_request: endpointData.cost_per_request,
auth: endpointData.auth || false,
methods: endpointData.methods || [],
}}
layout="vertical"
>
@@ -295,6 +319,31 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
/>
</Form.Item>
<Form.Item
label="HTTP Methods (Optional)"
name="methods"
extra={
selectedMethods.length === 0
? "All HTTP methods supported (default)"
: `Only ${selectedMethods.join(", ")} requests will be routed to this endpoint`
}
>
<Select
mode="multiple"
placeholder="Select methods (leave empty for all)"
value={selectedMethods}
onChange={setSelectedMethods}
allowClear
style={{ width: "100%" }}
>
{HTTP_METHODS.map((method) => (
<Option key={method} value={method}>
{method}
</Option>
))}
</Select>
</Form.Item>
<Form.Item label="Include Subpath" name="include_subpath" valuePropName="checked">
<Switch />
</Form.Item>
@@ -39,6 +39,7 @@ export interface passThroughItem {
include_subpath?: boolean;
cost_per_request?: number;
auth?: boolean;
methods?: string[];
guardrails?: Record<string, { request_fields?: string[]; response_fields?: string[] } | null>;
}
@@ -147,6 +148,32 @@ const PassThroughSettings: React.FC<GeneralSettingsPageProps> = ({ accessToken,
accessorKey: "target",
cell: (info: any) => <Text>{info.getValue()}</Text>,
},
{
header: () => (
<div className="flex items-center gap-1">
<span>Methods</span>
<Tooltip title="HTTP methods supported by this endpoint">
<InformationCircleIcon className="w-4 h-4 text-gray-400 cursor-help" />
</Tooltip>
</div>
),
accessorKey: "methods",
cell: (info: any) => {
const methods = info.getValue();
if (!methods || methods.length === 0) {
return <Badge color="blue">ALL</Badge>;
}
return (
<div className="flex flex-wrap gap-1">
{methods.map((method: string) => (
<Badge key={method} color="indigo" className="text-xs">
{method}
</Badge>
))}
</div>
);
},
},
{
header: () => (
<div className="flex items-center gap-1">