fix: API Request to use new fields and add DataInputs for Body,Header and Query
This commit is contained in:
parent
3d492db3d9
commit
3b6060b243
1 changed files with 65 additions and 32 deletions
|
|
@ -1,20 +1,26 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
from urllib.parse import urlencode, urlparse, parse_qsl, urlunparse
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.base.curl.parse import parse_context
|
from langflow.base.curl.parse import parse_context
|
||||||
from langflow.custom import Component
|
from langflow.custom import Component
|
||||||
from langflow.io import DropdownInput, IntInput, NestedDictInput, Output, TextInput
|
from langflow.io import DropdownInput, IntInput, NestedDictInput, Output, TextInput, DataInput
|
||||||
from langflow.schema import Data
|
from langflow.schema import Data
|
||||||
from langflow.schema.dotdict import dotdict
|
from langflow.schema.dotdict import dotdict
|
||||||
|
|
||||||
|
|
||||||
class APIRequestComponent(Component):
|
class APIRequestComponent(Component):
|
||||||
display_name = "API Request"
|
display_name = "API Request"
|
||||||
description = "Make HTTP requests given one or more URLs."
|
description = (
|
||||||
|
"This component allows you to make HTTP requests to one or more URLs. "
|
||||||
|
"You can provide headers and body as either dictionaries or Data objects. "
|
||||||
|
"Additionally, you can append query parameters to the URLs.\n\n"
|
||||||
|
"**Note:** Check advanced options for more settings."
|
||||||
|
)
|
||||||
icon = "Globe"
|
icon = "Globe"
|
||||||
|
|
||||||
inputs = [
|
inputs = [
|
||||||
|
|
@ -27,7 +33,7 @@ class APIRequestComponent(Component):
|
||||||
TextInput(
|
TextInput(
|
||||||
name="curl",
|
name="curl",
|
||||||
display_name="Curl",
|
display_name="Curl",
|
||||||
info="Paste a curl command to populate the fields.",
|
info="Paste a curl command to populate the fields. This will fill in the dictionary fields for headers and body.",
|
||||||
advanced=False,
|
advanced=False,
|
||||||
refresh_button=True,
|
refresh_button=True,
|
||||||
),
|
),
|
||||||
|
|
@ -36,17 +42,34 @@ class APIRequestComponent(Component):
|
||||||
display_name="Method",
|
display_name="Method",
|
||||||
options=["GET", "POST", "PATCH", "PUT"],
|
options=["GET", "POST", "PATCH", "PUT"],
|
||||||
value="GET",
|
value="GET",
|
||||||
info="The HTTP method to use.",
|
info="The HTTP method to use (GET, POST, PATCH, PUT).",
|
||||||
),
|
),
|
||||||
NestedDictInput(
|
DataInput(
|
||||||
name="headers",
|
name="headers",
|
||||||
display_name="Headers",
|
display_name="Headers Data",
|
||||||
info="The headers to send with the request.",
|
info="The headers to send with the request as a Data object.",
|
||||||
),
|
),
|
||||||
NestedDictInput(
|
NestedDictInput(
|
||||||
|
name="headers_dict",
|
||||||
|
display_name="Headers Dictionary",
|
||||||
|
info="The headers to send with the request as a dictionary. This is populated when using the CURL field.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
DataInput(
|
||||||
name="body",
|
name="body",
|
||||||
display_name="Body",
|
display_name="Body Data",
|
||||||
info="The body to send with the request (for POST, PATCH, PUT).",
|
info="The body to send with the request as a Data object (for POST, PATCH, PUT).",
|
||||||
|
),
|
||||||
|
NestedDictInput(
|
||||||
|
name="body_dict",
|
||||||
|
display_name="Body Dictionary",
|
||||||
|
info="The body to send with the request as a dictionary (for POST, PATCH, PUT). This is populated when using the CURL field.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
DataInput(
|
||||||
|
name="query_params",
|
||||||
|
display_name="Query Parameters",
|
||||||
|
info="The query parameters to append to the URL.",
|
||||||
),
|
),
|
||||||
IntInput(
|
IntInput(
|
||||||
name="timeout",
|
name="timeout",
|
||||||
|
|
@ -65,13 +88,16 @@ class APIRequestComponent(Component):
|
||||||
parsed = parse_context(curl)
|
parsed = parse_context(curl)
|
||||||
build_config["urls"]["value"] = [parsed.url]
|
build_config["urls"]["value"] = [parsed.url]
|
||||||
build_config["method"]["value"] = parsed.method.upper()
|
build_config["method"]["value"] = parsed.method.upper()
|
||||||
build_config["headers"]["value"] = dict(parsed.headers)
|
build_config["headers_dict"]["value"] = dict(parsed.headers)
|
||||||
|
|
||||||
try:
|
if parsed.data:
|
||||||
json_data = json.loads(parsed.data)
|
try:
|
||||||
build_config["body"]["value"] = json_data
|
json_data = json.loads(parsed.data)
|
||||||
except json.JSONDecodeError as e:
|
build_config["body_dict"]["value"] = json_data
|
||||||
print(e)
|
except json.JSONDecodeError as e:
|
||||||
|
logger.error(f"Error decoding JSON data: {e}")
|
||||||
|
else:
|
||||||
|
build_config["body_dict"]["value"] = None
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"Error parsing curl: {exc}")
|
logger.error(f"Error parsing curl: {exc}")
|
||||||
raise ValueError(f"Error parsing curl: {exc}")
|
raise ValueError(f"Error parsing curl: {exc}")
|
||||||
|
|
@ -80,6 +106,11 @@ class APIRequestComponent(Component):
|
||||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||||
if field_name == "curl" and field_value:
|
if field_name == "curl" and field_value:
|
||||||
build_config = self.parse_curl(field_value, build_config)
|
build_config = self.parse_curl(field_value, build_config)
|
||||||
|
# Apply the parsed values to the component fields
|
||||||
|
self.urls = build_config["urls"]["value"]
|
||||||
|
self.method = build_config["method"]["value"]
|
||||||
|
self.headers_dict = build_config["headers"]["value"]
|
||||||
|
self.body_dict = build_config["body"]["value"]
|
||||||
return build_config
|
return build_config
|
||||||
|
|
||||||
async def make_request(
|
async def make_request(
|
||||||
|
|
@ -96,7 +127,7 @@ class APIRequestComponent(Component):
|
||||||
raise ValueError(f"Unsupported method: {method}")
|
raise ValueError(f"Unsupported method: {method}")
|
||||||
|
|
||||||
data = body if body else None
|
data = body if body else None
|
||||||
payload = json.dumps(data)
|
payload = json.dumps(data) if data else None
|
||||||
try:
|
try:
|
||||||
response = await client.request(method, url, headers=headers, content=payload, timeout=timeout)
|
response = await client.request(method, url, headers=headers, content=payload, timeout=timeout)
|
||||||
try:
|
try:
|
||||||
|
|
@ -130,36 +161,38 @@ class APIRequestComponent(Component):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def add_query_params(self, url: str, params: dict) -> str:
|
||||||
|
url_parts = list(urlparse(url))
|
||||||
|
query = dict(parse_qsl(url_parts[4]))
|
||||||
|
query.update(params)
|
||||||
|
url_parts[4] = urlencode(query)
|
||||||
|
return urlunparse(url_parts)
|
||||||
|
|
||||||
async def make_requests(self) -> List[Data]:
|
async def make_requests(self) -> List[Data]:
|
||||||
method = self.method
|
method = self.method
|
||||||
urls = [url.strip() for url in self.urls if url.strip()]
|
urls = [url.strip() for url in self.urls if url.strip()]
|
||||||
curl = self.curl
|
curl = self.curl
|
||||||
headers = self.headers or {}
|
headers = self.headers if self.headers else (self.headers_dict or {})
|
||||||
body = self.body or {}
|
body = self.body if self.body else (self.body_dict or {})
|
||||||
timeout = self.timeout
|
timeout = self.timeout
|
||||||
|
query_params = self.query_params.data if self.query_params else {}
|
||||||
|
|
||||||
if curl:
|
if curl:
|
||||||
self._build_config = self.parse_curl(curl)
|
self._build_config = self.parse_curl(curl, dotdict())
|
||||||
|
|
||||||
if isinstance(headers, Data):
|
if isinstance(headers, Data):
|
||||||
headers_dict = headers.data
|
headers = headers.data
|
||||||
else:
|
|
||||||
headers_dict = headers
|
|
||||||
|
|
||||||
bodies = []
|
if isinstance(body, Data):
|
||||||
if body:
|
body = body.data
|
||||||
if not isinstance(body, list):
|
|
||||||
bodies = [body]
|
|
||||||
else:
|
|
||||||
bodies = body
|
|
||||||
bodies = [b.data if isinstance(b, Data) else b for b in bodies]
|
|
||||||
|
|
||||||
if len(urls) != len(bodies):
|
bodies = [body] * len(urls)
|
||||||
bodies += [None] * (len(urls) - len(bodies))
|
|
||||||
|
urls = [self.add_query_params(url, query_params) for url in urls]
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
results = await asyncio.gather(
|
results = await asyncio.gather(
|
||||||
*[self.make_request(client, method, u, headers_dict, rec, timeout) for u, rec in zip(urls, bodies)]
|
*[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)]
|
||||||
)
|
)
|
||||||
self.status = results
|
self.status = results
|
||||||
return results
|
return results
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue