fix: API Request to use new fields and add DataInputs for Body,Header and Query

This commit is contained in:
Cezar Vasconcelos 2024-06-19 20:54:58 +00:00
commit 3b6060b243

View file

@ -1,20 +1,26 @@
import asyncio
import json
from typing import Any, List, Optional
from urllib.parse import urlencode, urlparse, parse_qsl, urlunparse
import httpx
from loguru import logger
from langflow.base.curl.parse import parse_context
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.dotdict import dotdict
class APIRequestComponent(Component):
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"
inputs = [
@ -27,7 +33,7 @@ class APIRequestComponent(Component):
TextInput(
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,
refresh_button=True,
),
@ -36,17 +42,34 @@ class APIRequestComponent(Component):
display_name="Method",
options=["GET", "POST", "PATCH", "PUT"],
value="GET",
info="The HTTP method to use.",
info="The HTTP method to use (GET, POST, PATCH, PUT).",
),
NestedDictInput(
DataInput(
name="headers",
display_name="Headers",
info="The headers to send with the request.",
display_name="Headers Data",
info="The headers to send with the request as a Data object.",
),
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",
display_name="Body",
info="The body to send with the request (for POST, PATCH, PUT).",
display_name="Body Data",
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(
name="timeout",
@ -65,13 +88,16 @@ class APIRequestComponent(Component):
parsed = parse_context(curl)
build_config["urls"]["value"] = [parsed.url]
build_config["method"]["value"] = parsed.method.upper()
build_config["headers"]["value"] = dict(parsed.headers)
build_config["headers_dict"]["value"] = dict(parsed.headers)
try:
json_data = json.loads(parsed.data)
build_config["body"]["value"] = json_data
except json.JSONDecodeError as e:
print(e)
if parsed.data:
try:
json_data = json.loads(parsed.data)
build_config["body_dict"]["value"] = json_data
except json.JSONDecodeError as e:
logger.error(f"Error decoding JSON data: {e}")
else:
build_config["body_dict"]["value"] = None
except Exception as exc:
logger.error(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):
if field_name == "curl" and field_value:
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
async def make_request(
@ -96,7 +127,7 @@ class APIRequestComponent(Component):
raise ValueError(f"Unsupported method: {method}")
data = body if body else None
payload = json.dumps(data)
payload = json.dumps(data) if data else None
try:
response = await client.request(method, url, headers=headers, content=payload, timeout=timeout)
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]:
method = self.method
urls = [url.strip() for url in self.urls if url.strip()]
curl = self.curl
headers = self.headers or {}
body = self.body or {}
headers = self.headers if self.headers else (self.headers_dict or {})
body = self.body if self.body else (self.body_dict or {})
timeout = self.timeout
query_params = self.query_params.data if self.query_params else {}
if curl:
self._build_config = self.parse_curl(curl)
self._build_config = self.parse_curl(curl, dotdict())
if isinstance(headers, Data):
headers_dict = headers.data
else:
headers_dict = headers
headers = headers.data
bodies = []
if body:
if not isinstance(body, list):
bodies = [body]
else:
bodies = body
bodies = [b.data if isinstance(b, Data) else b for b in bodies]
if isinstance(body, Data):
body = body.data
if len(urls) != len(bodies):
bodies += [None] * (len(urls) - len(bodies))
bodies = [body] * len(urls)
urls = [self.add_query_params(url, query_params) for url in urls]
async with httpx.AsyncClient() as client:
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
return results